covdet.c
Go to the documentation of this file.
00001 
00008 /*
00009 Copyright (C) 2013-14 Andrea Vedaldi.
00010 Copyright (C) 2012 Karel Lenc, Andrea Vedaldi and Michal Perdoch.
00011 All rights reserved.
00012 
00013 This file is part of the VLFeat library and is made available under
00014 the terms of the BSD license (see the COPYING file).
00015 */
00016 
00945 #include "covdet.h"
00946 #include <string.h>
00947 
00955 static int
00956 _vl_resize_buffer (void ** buffer, vl_size * bufferSize, vl_size targetSize) {
00957   void * newBuffer ;
00958   if (*buffer == NULL) {
00959     *buffer = vl_malloc(targetSize) ;
00960     if (*buffer) {
00961       *bufferSize = targetSize ;
00962       return VL_ERR_OK ;
00963     } else {
00964       *bufferSize = 0 ;
00965       return VL_ERR_ALLOC ;
00966     }
00967   }
00968   newBuffer = vl_realloc(*buffer, targetSize) ;
00969   if (newBuffer) {
00970     *buffer = newBuffer ;
00971     *bufferSize = targetSize ;
00972     return VL_ERR_OK ;
00973   } else {
00974     return VL_ERR_ALLOC ;
00975   }
00976 }
00977 
00985 static int
00986 _vl_enlarge_buffer (void ** buffer, vl_size * bufferSize, vl_size targetSize) {
00987   if (*bufferSize >= targetSize) return VL_ERR_OK ;
00988   return _vl_resize_buffer(buffer,bufferSize,targetSize) ;
00989 }
00990 
00991 /* ---------------------------------------------------------------- */
00992 /*                                            Finding local extrema */
00993 /* ---------------------------------------------------------------- */
00994 
00995 /* Todo: make this generally available in the library */
00996 
00997 typedef struct _VlCovDetExtremum2
00998 {
00999   vl_index xi ;
01000   vl_index yi ;
01001   float x ;
01002   float y ;
01003   float peakScore ;
01004   float edgeScore ;
01005 } VlCovDetExtremum2 ;
01006 
01007 typedef struct _VlCovDetExtremum3
01008 {
01009   vl_index xi ;
01010   vl_index yi ;
01011   vl_index zi ;
01012   float x ;
01013   float y ;
01014   float z ;
01015   float peakScore ;
01016   float edgeScore ;
01017 } VlCovDetExtremum3 ;
01018 
01019 VL_EXPORT vl_size
01020 vl_find_local_extrema_3 (vl_index ** extrema, vl_size * bufferSize,
01021                          float const * map,
01022                          vl_size width, vl_size height, vl_size depth,
01023                          double threshold) ;
01024 
01025 VL_EXPORT vl_size
01026 vl_find_local_extrema_2 (vl_index ** extrema, vl_size * bufferSize,
01027                          float const * map,
01028                          vl_size width, vl_size height,
01029                          double threshold) ;
01030 
01031 VL_EXPORT vl_bool
01032 vl_refine_local_extreum_3 (VlCovDetExtremum3 * refined,
01033                            float const * map,
01034                            vl_size width, vl_size height, vl_size depth,
01035                            vl_index x, vl_index y, vl_index z) ;
01036 
01037 VL_EXPORT vl_bool
01038 vl_refine_local_extreum_2 (VlCovDetExtremum2 * refined,
01039                            float const * map,
01040                            vl_size width, vl_size height,
01041                            vl_index x, vl_index y) ;
01042 
01056 vl_size
01057 vl_find_local_extrema_3 (vl_index ** extrema, vl_size * bufferSize,
01058                          float const * map,
01059                          vl_size width, vl_size height, vl_size depth,
01060                          double threshold)
01061 {
01062   vl_index x, y, z ;
01063   vl_size const xo = 1 ;
01064   vl_size const yo = width ;
01065   vl_size const zo = width * height ;
01066   float const *pt = map + xo + yo + zo ;
01067 
01068   vl_size numExtrema = 0 ;
01069   vl_size requiredSize = 0 ;
01070 
01071 #define CHECK_NEIGHBORS_3(v,CMP,SGN)     (\
01072 v CMP ## = SGN threshold &&               \
01073 v CMP *(pt + xo) &&                       \
01074 v CMP *(pt - xo) &&                       \
01075 v CMP *(pt + zo) &&                       \
01076 v CMP *(pt - zo) &&                       \
01077 v CMP *(pt + yo) &&                       \
01078 v CMP *(pt - yo) &&                       \
01079 \
01080 v CMP *(pt + yo + xo) &&                  \
01081 v CMP *(pt + yo - xo) &&                  \
01082 v CMP *(pt - yo + xo) &&                  \
01083 v CMP *(pt - yo - xo) &&                  \
01084 \
01085 v CMP *(pt + xo      + zo) &&             \
01086 v CMP *(pt - xo      + zo) &&             \
01087 v CMP *(pt + yo      + zo) &&             \
01088 v CMP *(pt - yo      + zo) &&             \
01089 v CMP *(pt + yo + xo + zo) &&             \
01090 v CMP *(pt + yo - xo + zo) &&             \
01091 v CMP *(pt - yo + xo + zo) &&             \
01092 v CMP *(pt - yo - xo + zo) &&             \
01093 \
01094 v CMP *(pt + xo      - zo) &&             \
01095 v CMP *(pt - xo      - zo) &&             \
01096 v CMP *(pt + yo      - zo) &&             \
01097 v CMP *(pt - yo      - zo) &&             \
01098 v CMP *(pt + yo + xo - zo) &&             \
01099 v CMP *(pt + yo - xo - zo) &&             \
01100 v CMP *(pt - yo + xo - zo) &&             \
01101 v CMP *(pt - yo - xo - zo) )
01102 
01103   for (z = 1 ; z < (signed)depth - 1 ; ++z) {
01104     for (y = 1 ; y < (signed)height - 1 ; ++y) {
01105       for (x = 1 ; x < (signed)width - 1 ; ++x) {
01106         float value = *pt ;
01107         if (CHECK_NEIGHBORS_3(value,>,+) || CHECK_NEIGHBORS_3(value,<,-)) {
01108           numExtrema ++ ;
01109           requiredSize += sizeof(vl_index) * 3 ;
01110           if (*bufferSize < requiredSize) {
01111             int err = _vl_resize_buffer((void**)extrema, bufferSize,
01112                                         requiredSize + 2000 * 3 * sizeof(vl_index)) ;
01113             if (err != VL_ERR_OK) abort() ;
01114           }
01115           (*extrema) [3 * (numExtrema - 1) + 0] = x ;
01116           (*extrema) [3 * (numExtrema - 1) + 1] = y ;
01117           (*extrema) [3 * (numExtrema - 1) + 2] = z ;
01118         }
01119         pt += xo ;
01120       }
01121       pt += 2*xo ;
01122     }
01123     pt += 2*yo ;
01124   }
01125   return numExtrema ;
01126 }
01127 
01146 vl_size
01147 vl_find_local_extrema_2 (vl_index ** extrema, vl_size * bufferSize,
01148                          float const* map,
01149                          vl_size width, vl_size height,
01150                          double threshold)
01151 {
01152   vl_index x, y ;
01153   vl_size const xo = 1 ;
01154   vl_size const yo = width ;
01155   float const *pt = map + xo + yo ;
01156 
01157   vl_size numExtrema = 0 ;
01158   vl_size requiredSize = 0 ;
01159 #define CHECK_NEIGHBORS_2(v,CMP,SGN)     (\
01160 v CMP ## = SGN threshold &&               \
01161 v CMP *(pt + xo) &&                       \
01162 v CMP *(pt - xo) &&                       \
01163 v CMP *(pt + yo) &&                       \
01164 v CMP *(pt - yo) &&                       \
01165 \
01166 v CMP *(pt + yo + xo) &&                  \
01167 v CMP *(pt + yo - xo) &&                  \
01168 v CMP *(pt - yo + xo) &&                  \
01169 v CMP *(pt - yo - xo) )
01170 
01171   for (y = 1 ; y < (signed)height - 1 ; ++y) {
01172     for (x = 1 ; x < (signed)width - 1 ; ++x) {
01173       float value = *pt ;
01174       if (CHECK_NEIGHBORS_2(value,>,+) || CHECK_NEIGHBORS_2(value,<,-)) {
01175         numExtrema ++ ;
01176         requiredSize += sizeof(vl_index) * 2 ;
01177         if (*bufferSize < requiredSize) {
01178           int err = _vl_resize_buffer((void**)extrema, bufferSize,
01179                                       requiredSize + 2000 * 2 * sizeof(vl_index)) ;
01180           if (err != VL_ERR_OK) abort() ;
01181         }
01182         (*extrema) [2 * (numExtrema - 1) + 0] = x ;
01183         (*extrema) [2 * (numExtrema - 1) + 1] = y ;
01184       }
01185       pt += xo ;
01186     }
01187     pt += 2*xo ;
01188   }
01189   return numExtrema ;
01190 }
01191 
01205 VL_EXPORT vl_bool
01206 vl_refine_local_extreum_3 (VlCovDetExtremum3 * refined,
01207                            float const * map,
01208                            vl_size width, vl_size height, vl_size depth,
01209                            vl_index x, vl_index y, vl_index z)
01210 {
01211   vl_size const xo = 1 ;
01212   vl_size const yo = width ;
01213   vl_size const zo = width * height ;
01214 
01215   double Dx=0,Dy=0,Dz=0,Dxx=0,Dyy=0,Dzz=0,Dxy=0,Dxz=0,Dyz=0 ;
01216   double A [3*3], b [3] ;
01217 
01218 #define at(dx,dy,dz) (*(pt + (dx)*xo + (dy)*yo + (dz)*zo))
01219 #define Aat(i,j) (A[(i)+(j)*3])
01220 
01221   float const * pt ;
01222   vl_index dx = 0 ;
01223   vl_index dy = 0 ;
01224   /*vl_index dz = 0 ;*/
01225   vl_index iter ;
01226   int err ;
01227 
01228   assert (map) ;
01229   assert (1 <= x && x <= (signed)width - 2) ;
01230   assert (1 <= y && y <= (signed)height - 2) ;
01231   assert (1 <= z && z <= (signed)depth - 2) ;
01232 
01233   for (iter = 0 ; iter < 5 ; ++iter) {
01234     x += dx ;
01235     y += dy ;
01236     pt = map + x*xo + y*yo + z*zo ;
01237 
01238     /* compute the gradient */
01239     Dx = 0.5 * (at(+1,0,0) - at(-1,0,0)) ;
01240     Dy = 0.5 * (at(0,+1,0) - at(0,-1,0));
01241     Dz = 0.5 * (at(0,0,+1) - at(0,0,-1)) ;
01242 
01243     /* compute the Hessian */
01244     Dxx = (at(+1,0,0) + at(-1,0,0) - 2.0 * at(0,0,0)) ;
01245     Dyy = (at(0,+1,0) + at(0,-1,0) - 2.0 * at(0,0,0)) ;
01246     Dzz = (at(0,0,+1) + at(0,0,-1) - 2.0 * at(0,0,0)) ;
01247 
01248     Dxy = 0.25 * (at(+1,+1,0) + at(-1,-1,0) - at(-1,+1,0) - at(+1,-1,0)) ;
01249     Dxz = 0.25 * (at(+1,0,+1) + at(-1,0,-1) - at(-1,0,+1) - at(+1,0,-1)) ;
01250     Dyz = 0.25 * (at(0,+1,+1) + at(0,-1,-1) - at(0,-1,+1) - at(0,+1,-1)) ;
01251 
01252     /* solve linear system */
01253     Aat(0,0) = Dxx ;
01254     Aat(1,1) = Dyy ;
01255     Aat(2,2) = Dzz ;
01256     Aat(0,1) = Aat(1,0) = Dxy ;
01257     Aat(0,2) = Aat(2,0) = Dxz ;
01258     Aat(1,2) = Aat(2,1) = Dyz ;
01259 
01260     b[0] = - Dx ;
01261     b[1] = - Dy ;
01262     b[2] = - Dz ;
01263 
01264     err = vl_solve_linear_system_3(b, A, b) ;
01265 
01266     if (err != VL_ERR_OK) {
01267       b[0] = 0 ;
01268       b[1] = 0 ;
01269       b[2] = 0 ;
01270       break ;
01271     }
01272 
01273     /* Keep going if there is sufficient translation */
01274 
01275     dx = (b[0] > 0.6 && x < (signed)width - 2 ?  1 : 0)
01276     + (b[0] < -0.6 && x > 1 ? -1 : 0) ;
01277 
01278     dy = (b[1] > 0.6 && y < (signed)height - 2 ?  1 : 0)
01279     + (b[1] < -0.6 && y > 1 ? -1 : 0) ;
01280 
01281     if (dx == 0 && dy == 0) break ;
01282   }
01283 
01284   /* check threshold and other conditions */
01285   {
01286     double peakScore = at(0,0,0)
01287     + 0.5 * (Dx * b[0] + Dy * b[1] + Dz * b[2]) ;
01288     double alpha = (Dxx+Dyy)*(Dxx+Dyy) / (Dxx*Dyy - Dxy*Dxy) ;
01289     double edgeScore ;
01290 
01291     if (alpha < 0) {
01292       /* not an extremum */
01293       edgeScore = VL_INFINITY_D ;
01294     } else {
01295       edgeScore = (0.5*alpha - 1) + sqrt(VL_MAX(0.25*alpha - 1,0)*alpha) ;
01296     }
01297 
01298     refined->xi = x ;
01299     refined->yi = y ;
01300     refined->zi = z ;
01301     refined->x = x + b[0] ;
01302     refined->y = y + b[1] ;
01303     refined->z = z + b[2] ;
01304     refined->peakScore = peakScore ;
01305     refined->edgeScore = edgeScore ;
01306 
01307     return
01308     err == VL_ERR_OK &&
01309     vl_abs_d(b[0]) < 1.5 &&
01310     vl_abs_d(b[1]) < 1.5 &&
01311     vl_abs_d(b[2]) < 1.5 &&
01312     0 <= refined->x && refined->x <= (signed)width - 1 &&
01313     0 <= refined->y && refined->y <= (signed)height - 1 &&
01314     0 <= refined->z && refined->z <= (signed)depth - 1 ;
01315   }
01316 #undef Aat
01317 #undef at
01318 }
01319 
01331 VL_EXPORT vl_bool
01332 vl_refine_local_extreum_2 (VlCovDetExtremum2 * refined,
01333                            float const * map,
01334                            vl_size width, vl_size height,
01335                            vl_index x, vl_index y)
01336 {
01337   vl_size const xo = 1 ;
01338   vl_size const yo = width ;
01339 
01340   double Dx=0,Dy=0,Dxx=0,Dyy=0,Dxy=0;
01341   double A [2*2], b [2] ;
01342 
01343 #define at(dx,dy) (*(pt + (dx)*xo + (dy)*yo ))
01344 #define Aat(i,j) (A[(i)+(j)*2])
01345 
01346   float const * pt ;
01347   vl_index dx = 0 ;
01348   vl_index dy = 0 ;
01349   vl_index iter ;
01350   int err ;
01351 
01352   assert (map) ;
01353   assert (1 <= x && x <= (signed)width - 2) ;
01354   assert (1 <= y && y <= (signed)height - 2) ;
01355 
01356   for (iter = 0 ; iter < 5 ; ++iter) {
01357     x += dx ;
01358     y += dy ;
01359     pt = map + x*xo + y*yo  ;
01360 
01361     /* compute the gradient */
01362     Dx = 0.5 * (at(+1,0) - at(-1,0)) ;
01363     Dy = 0.5 * (at(0,+1) - at(0,-1));
01364 
01365     /* compute the Hessian */
01366     Dxx = (at(+1,0) + at(-1,0) - 2.0 * at(0,0)) ;
01367     Dyy = (at(0,+1) + at(0,-1) - 2.0 * at(0,0)) ;
01368     Dxy = 0.25 * (at(+1,+1) + at(-1,-1) - at(-1,+1) - at(+1,-1)) ;
01369 
01370     /* solve linear system */
01371     Aat(0,0) = Dxx ;
01372     Aat(1,1) = Dyy ;
01373     Aat(0,1) = Aat(1,0) = Dxy ;
01374 
01375     b[0] = - Dx ;
01376     b[1] = - Dy ;
01377 
01378     err = vl_solve_linear_system_2(b, A, b) ;
01379 
01380     if (err != VL_ERR_OK) {
01381       b[0] = 0 ;
01382       b[1] = 0 ;
01383       break ;
01384     }
01385 
01386     /* Keep going if there is sufficient translation */
01387 
01388     dx = (b[0] > 0.6 && x < (signed)width - 2 ?  1 : 0)
01389     + (b[0] < -0.6 && x > 1 ? -1 : 0) ;
01390 
01391     dy = (b[1] > 0.6 && y < (signed)height - 2 ?  1 : 0)
01392     + (b[1] < -0.6 && y > 1 ? -1 : 0) ;
01393 
01394     if (dx == 0 && dy == 0) break ;
01395   }
01396 
01397   /* check threshold and other conditions */
01398   {
01399     double peakScore = at(0,0) + 0.5 * (Dx * b[0] + Dy * b[1]) ;
01400     double alpha = (Dxx+Dyy)*(Dxx+Dyy) / (Dxx*Dyy - Dxy*Dxy) ;
01401     double edgeScore ;
01402 
01403     if (alpha < 0) {
01404       /* not an extremum */
01405       edgeScore = VL_INFINITY_D ;
01406     } else {
01407       edgeScore = (0.5*alpha - 1) + sqrt(VL_MAX(0.25*alpha - 1,0)*alpha) ;
01408     }
01409 
01410     refined->xi = x ;
01411     refined->yi = y ;
01412     refined->x = x + b[0] ;
01413     refined->y = y + b[1] ;
01414     refined->peakScore = peakScore ;
01415     refined->edgeScore = edgeScore ;
01416 
01417     return
01418     err == VL_ERR_OK &&
01419     vl_abs_d(b[0]) < 1.5 &&
01420     vl_abs_d(b[1]) < 1.5 &&
01421     0 <= refined->x && refined->x <= (signed)width - 1 &&
01422     0 <= refined->y && refined->y <= (signed)height - 1 ;
01423   }
01424 #undef Aat
01425 #undef at
01426 }
01427 
01428 /* ---------------------------------------------------------------- */
01429 /*                                                Covarant detector */
01430 /* ---------------------------------------------------------------- */
01431 
01432 #define VL_COVDET_MAX_NUM_ORIENTATIONS 4
01433 #define VL_COVDET_MAX_NUM_LAPLACIAN_SCALES 4
01434 #define VL_COVDET_AA_PATCH_RESOLUTION 20
01435 #define VL_COVDET_AA_MAX_NUM_ITERATIONS 15
01436 #define VL_COVDET_OR_NUM_ORIENTATION_HISTOGAM_BINS 36
01437 #define VL_COVDET_AA_RELATIVE_INTEGRATION_SIGMA 3
01438 #define VL_COVDET_AA_RELATIVE_DERIVATIVE_SIGMA 1
01439 #define VL_COVDET_AA_MAX_ANISOTROPY 5
01440 #define VL_COVDET_AA_CONVERGENCE_THRESHOLD 1.001
01441 #define VL_COVDET_AA_ACCURATE_SMOOTHING VL_FALSE
01442 #define VL_COVDET_AA_PATCH_EXTENT (3*VL_COVDET_AA_RELATIVE_INTEGRATION_SIGMA)
01443 #define VL_COVDET_OR_ADDITIONAL_PEAKS_RELATIVE_SIZE 0.8
01444 #define VL_COVDET_LAP_NUM_LEVELS 10
01445 #define VL_COVDET_LAP_PATCH_RESOLUTION 16
01446 #define VL_COVDET_LAP_DEF_PEAK_THRESHOLD 0.01
01447 #define VL_COVDET_DOG_DEF_PEAK_THRESHOLD VL_COVDET_LAP_DEF_PEAK_THRESHOLD
01448 #define VL_COVDET_DOG_DEF_EDGE_THRESHOLD 10.0
01449 #define VL_COVDET_HARRIS_DEF_PEAK_THRESHOLD 0.000002
01450 #define VL_COVDET_HARRIS_DEF_EDGE_THRESHOLD 10.0
01451 #define VL_COVDET_HESSIAN_DEF_PEAK_THRESHOLD 0.003
01452 #define VL_COVDET_HESSIAN_DEF_EDGE_THRESHOLD 10.0
01453 
01455 struct _VlCovDet
01456 {
01457   VlScaleSpace *gss ;        
01458   VlScaleSpace *css ;        
01459   VlCovDetMethod method ;    
01460   double peakThreshold ;     
01461   double edgeThreshold ;     
01462   double lapPeakThreshold;   
01463   vl_size octaveResolution ; 
01464   vl_index firstOctave ;     
01466   double nonExtremaSuppression ;
01467   vl_size numNonExtremaSuppressed ;
01468 
01469   VlCovDetFeature *features ;
01470   vl_size numFeatures ;
01471   vl_size numFeatureBufferSize ;
01472 
01473   float * patch ;
01474   vl_size patchBufferSize ;
01475 
01476   vl_bool transposed ;
01477   VlCovDetFeatureOrientation orientations [VL_COVDET_MAX_NUM_ORIENTATIONS] ;
01478   VlCovDetFeatureLaplacianScale scales [VL_COVDET_MAX_NUM_LAPLACIAN_SCALES] ;
01479 
01480   vl_bool aaAccurateSmoothing ;
01481   float aaPatch [(2*VL_COVDET_AA_PATCH_RESOLUTION+1)*(2*VL_COVDET_AA_PATCH_RESOLUTION+1)] ;
01482   float aaPatchX [(2*VL_COVDET_AA_PATCH_RESOLUTION+1)*(2*VL_COVDET_AA_PATCH_RESOLUTION+1)] ;
01483   float aaPatchY [(2*VL_COVDET_AA_PATCH_RESOLUTION+1)*(2*VL_COVDET_AA_PATCH_RESOLUTION+1)] ;
01484   float aaMask [(2*VL_COVDET_AA_PATCH_RESOLUTION+1)*(2*VL_COVDET_AA_PATCH_RESOLUTION+1)] ;
01485 
01486   float lapPatch [(2*VL_COVDET_LAP_PATCH_RESOLUTION+1)*(2*VL_COVDET_LAP_PATCH_RESOLUTION+1)] ;
01487   float laplacians [(2*VL_COVDET_LAP_PATCH_RESOLUTION+1)*(2*VL_COVDET_LAP_PATCH_RESOLUTION+1)*VL_COVDET_LAP_NUM_LEVELS] ;
01488   vl_size numFeaturesWithNumScales [VL_COVDET_MAX_NUM_LAPLACIAN_SCALES + 1] ;
01489 }  ;
01490 
01491 VlEnumerator vlCovdetMethods [VL_COVDET_METHOD_NUM] = {
01492   {"DoG" ,              (vl_index)VL_COVDET_METHOD_DOG               },
01493   {"Hessian",           (vl_index)VL_COVDET_METHOD_HESSIAN           },
01494   {"HessianLaplace",    (vl_index)VL_COVDET_METHOD_HESSIAN_LAPLACE   },
01495   {"HarrisLaplace",     (vl_index)VL_COVDET_METHOD_HARRIS_LAPLACE    },
01496   {"MultiscaleHessian", (vl_index)VL_COVDET_METHOD_MULTISCALE_HESSIAN},
01497   {"MultiscaleHarris",  (vl_index)VL_COVDET_METHOD_MULTISCALE_HARRIS },
01498   {0,                   0                                            }
01499 } ;
01500 
01506 VlCovDet *
01507 vl_covdet_new (VlCovDetMethod method)
01508 {
01509   VlCovDet * self = vl_calloc(sizeof(VlCovDet),1) ;
01510   self->method = method ;
01511   self->octaveResolution = 3 ;
01512   self->firstOctave = -1 ;
01513   switch (self->method) {
01514     case VL_COVDET_METHOD_DOG :
01515       self->peakThreshold = VL_COVDET_DOG_DEF_PEAK_THRESHOLD ;
01516       self->edgeThreshold = VL_COVDET_DOG_DEF_EDGE_THRESHOLD ;
01517       self->lapPeakThreshold = 0  ; /* not used */
01518       break ;
01519     case VL_COVDET_METHOD_HARRIS_LAPLACE:
01520     case VL_COVDET_METHOD_MULTISCALE_HARRIS:
01521       self->peakThreshold = VL_COVDET_HARRIS_DEF_PEAK_THRESHOLD ;
01522       self->edgeThreshold = VL_COVDET_HARRIS_DEF_EDGE_THRESHOLD ;
01523       self->lapPeakThreshold = VL_COVDET_LAP_DEF_PEAK_THRESHOLD ;
01524       break ;
01525     case VL_COVDET_METHOD_HESSIAN :
01526     case VL_COVDET_METHOD_HESSIAN_LAPLACE:
01527     case VL_COVDET_METHOD_MULTISCALE_HESSIAN:
01528       self->peakThreshold = VL_COVDET_HESSIAN_DEF_PEAK_THRESHOLD ;
01529       self->edgeThreshold = VL_COVDET_HESSIAN_DEF_EDGE_THRESHOLD ;
01530       self->lapPeakThreshold = VL_COVDET_LAP_DEF_PEAK_THRESHOLD ;
01531       break;
01532     default:
01533       assert(0) ;
01534   }
01535 
01536   self->nonExtremaSuppression = 0.5 ;
01537   self->features = NULL ;
01538   self->numFeatures = 0 ;
01539   self->numFeatureBufferSize = 0 ;
01540   self->patch = NULL ;
01541   self->patchBufferSize = 0 ;
01542   self->transposed = VL_FALSE ;
01543   self->aaAccurateSmoothing = VL_COVDET_AA_ACCURATE_SMOOTHING ;
01544 
01545   {
01546     vl_index const w = VL_COVDET_AA_PATCH_RESOLUTION ;
01547     vl_index i,j ;
01548     double step = (2.0 * VL_COVDET_AA_PATCH_EXTENT) / (2*w+1) ;
01549     double sigma = VL_COVDET_AA_RELATIVE_INTEGRATION_SIGMA ;
01550     for (j = -w ; j <= w ; ++j) {
01551       for (i = -w ; i <= w ; ++i) {
01552         double dx = i*step/sigma ;
01553         double dy = j*step/sigma ;
01554         self->aaMask[(i+w) + (2*w+1)*(j+w)] = exp(-0.5*(dx*dx+dy*dy)) ;
01555       }
01556     }
01557   }
01558 
01559   {
01560     /*
01561      Covers one octave of Laplacian filters, from sigma=1 to sigma=2.
01562      The spatial sampling step is 0.5.
01563      */
01564     vl_index s ;
01565     for (s = 0 ; s < VL_COVDET_LAP_NUM_LEVELS ; ++s) {
01566       double sigmaLap = pow(2.0, -0.5 +
01567                             (double)s / (VL_COVDET_LAP_NUM_LEVELS - 1)) ;
01568       double const sigmaImage = 1.0 / sqrt(2.0) ;
01569       double const step = 0.5 * sigmaImage ;
01570       double const sigmaDelta = sqrt(sigmaLap*sigmaLap - sigmaImage*sigmaImage) ;
01571       vl_size const w = VL_COVDET_LAP_PATCH_RESOLUTION ;
01572       vl_size const num = 2 * w + 1  ;
01573       float * pt = self->laplacians + s * (num * num) ;
01574 
01575       memset(pt, 0, num * num * sizeof(float)) ;
01576 
01577 #define at(x,y) pt[(x+w)+(y+w)*(2*w+1)]
01578       at(0,0) = - 4.0 ;
01579       at(-1,0) = 1.0 ;
01580       at(+1,0) = 1.0 ;
01581       at(0,1) = 1.0 ;
01582       at(0,-1) = 1.0 ;
01583 #undef at
01584 
01585       vl_imsmooth_f(pt, num,
01586                     pt, num, num, num,
01587                     sigmaDelta / step, sigmaDelta / step) ;
01588 
01589 #if 0
01590       {
01591         char name [200] ;
01592         snprintf(name, 200, "/tmp/%f-lap.pgm", sigmaDelta) ;
01593         vl_pgm_write_f(name, pt, num, num) ;
01594       }
01595 #endif
01596 
01597     }
01598   }
01599   return self ;
01600 }
01601 
01609 void
01610 vl_covdet_reset (VlCovDet * self)
01611 {
01612   if (self->features) {
01613     vl_free(self->features) ;
01614     self->features = NULL ;
01615   }
01616   if (self->css) {
01617     vl_scalespace_delete(self->css) ;
01618     self->css = NULL ;
01619   }
01620   if (self->gss) {
01621     vl_scalespace_delete(self->gss) ;
01622     self->gss = NULL ;
01623   }
01624 }
01625 
01630 void
01631 vl_covdet_delete (VlCovDet * self)
01632 {
01633   vl_covdet_reset(self) ;
01634   if (self->patch) vl_free (self->patch) ;
01635   vl_free(self) ;
01636 }
01637 
01647 int
01648 vl_covdet_append_feature (VlCovDet * self, VlCovDetFeature const * feature)
01649 {
01650   vl_size requiredSize ;
01651   assert(self) ;
01652   assert(feature) ;
01653   self->numFeatures ++ ;
01654   requiredSize = self->numFeatures * sizeof(VlCovDetFeature) ;
01655   if (requiredSize > self->numFeatureBufferSize) {
01656     int err = _vl_resize_buffer((void**)&self->features, &self->numFeatureBufferSize,
01657                                 (self->numFeatures + 1000) * sizeof(VlCovDetFeature)) ;
01658     if (err) {
01659       self->numFeatures -- ;
01660       return err ;
01661     }
01662   }
01663   self->features[self->numFeatures - 1] = *feature ;
01664   return VL_ERR_OK ;
01665 }
01666 
01667 /* ---------------------------------------------------------------- */
01668 /*                                              Process a new image */
01669 /* ---------------------------------------------------------------- */
01670 
01682 int
01683 vl_covdet_put_image (VlCovDet * self,
01684                      float const * image,
01685                      vl_size width, vl_size height)
01686 {
01687   vl_size const minOctaveSize = 16 ;
01688   vl_index lastOctave ;
01689   vl_index octaveFirstSubdivision ;
01690   vl_index octaveLastSubdivision ;
01691   VlScaleSpaceGeometry geom = vl_scalespace_get_default_geometry(width,height) ;
01692 
01693   assert (self) ;
01694   assert (image) ;
01695   assert (width >= 1) ;
01696   assert (height >= 1) ;
01697 
01698   /* (minOctaveSize - 1) 2^lastOctave <= min(width,height) - 1 */
01699   lastOctave = vl_floor_d(vl_log2_d(VL_MIN((double)width-1,(double)height-1) / (minOctaveSize - 1))) ;
01700 
01701   if (self->method == VL_COVDET_METHOD_DOG) {
01702     octaveFirstSubdivision = -1 ;
01703     octaveLastSubdivision = self->octaveResolution + 1 ;
01704   } else if (self->method == VL_COVDET_METHOD_HESSIAN) {
01705     octaveFirstSubdivision = -1 ;
01706     octaveLastSubdivision = self->octaveResolution ;
01707   } else {
01708     octaveFirstSubdivision = 0 ;
01709     octaveLastSubdivision = self->octaveResolution - 1 ;
01710   }
01711 
01712   geom.width = width ;
01713   geom.height = height ;
01714   geom.firstOctave = self->firstOctave ;
01715   geom.lastOctave = lastOctave ;
01716   geom.octaveResolution = self->octaveResolution ;
01717   geom.octaveFirstSubdivision = octaveFirstSubdivision ;
01718   geom.octaveLastSubdivision = octaveLastSubdivision ;
01719 
01720   if (self->gss == NULL ||
01721       ! vl_scalespacegeometry_is_equal (geom,
01722                                         vl_scalespace_get_geometry(self->gss)))
01723   {
01724     if (self->gss) vl_scalespace_delete(self->gss) ;
01725     self->gss = vl_scalespace_new_with_geometry(geom) ;
01726     if (self->gss == NULL) return VL_ERR_ALLOC ;
01727   }
01728   vl_scalespace_put_image(self->gss, image) ;
01729   return VL_ERR_OK ;
01730 }
01731 
01732 /* ---------------------------------------------------------------- */
01733 /*                                              Cornerness measures */
01734 /* ---------------------------------------------------------------- */
01735 
01745 static void
01746 _vl_det_hessian_response (float * hessian,
01747                           float const * image,
01748                           vl_size width, vl_size height,
01749                           double step, double sigma)
01750 {
01751   float factor = (float) pow(sigma/step, 4.0) ;
01752   vl_index const xo = 1 ; /* x-stride */
01753   vl_index const yo = width;  /* y-stride */
01754   vl_size r, c;
01755 
01756   float p11, p12, p13, p21, p22, p23, p31, p32, p33;
01757 
01758   /* setup input pointer to be centered at 0,1 */
01759   float const *in = image + yo ;
01760 
01761   /* setup output pointer to be centered at 1,1 */
01762   float *out = hessian + xo + yo;
01763 
01764   /* move 3x3 window and convolve */
01765   for (r = 1; r < height - 1; ++r)
01766   {
01767     /* fill in shift registers at the beginning of the row */
01768     p11 = in[-yo]; p12 = in[xo - yo];
01769     p21 = in[  0]; p22 = in[xo     ];
01770     p31 = in[+yo]; p32 = in[xo + yo];
01771     /* setup input pointer to (2,1) of the 3x3 square */
01772     in += 2;
01773     for (c = 1; c < width - 1; ++c)
01774     {
01775       float Lxx, Lyy, Lxy;
01776       /* fetch remaining values (last column) */
01777       p13 = in[-yo]; p23 = *in; p33 = in[+yo];
01778 
01779       /* Compute 3x3 Hessian values from pixel differences. */
01780       Lxx = (-p21 + 2*p22 - p23);
01781       Lyy = (-p12 + 2*p22 - p32);
01782       Lxy = ((p11 - p31 - p13 + p33)/4.0f);
01783 
01784       /* normalize and write out */
01785       *out = (Lxx * Lyy - Lxy * Lxy) * factor ;
01786 
01787       /* move window */
01788       p11=p12; p12=p13;
01789       p21=p22; p22=p23;
01790       p31=p32; p32=p33;
01791 
01792       /* move input/output pointers */
01793       in++; out++;
01794     }
01795     out += 2;
01796   }
01797 
01798   /* Copy the computed values to borders */
01799   in = hessian + yo + xo ;
01800   out = hessian + xo ;
01801 
01802   /* Top row without corners */
01803   memcpy(out, in, (width - 2)*sizeof(float));
01804   out--;
01805   in -= yo;
01806 
01807   /* Left border columns without last row */
01808   for (r = 0; r < height - 1; r++){
01809     *out = *in;
01810     *(out + yo - 1) = *(in + yo - 3);
01811     in += yo;
01812     out += yo;
01813   }
01814 
01815   /* Bottom corners */
01816   in -= yo;
01817   *out = *in;
01818   *(out + yo - 1) = *(in + yo - 3);
01819 
01820   /* Bottom row without corners */
01821   out++;
01822   memcpy(out, in, (width - 2)*sizeof(float));
01823 }
01824 
01836 static void
01837 _vl_harris_response (float * harris,
01838                      float const * image,
01839                      vl_size width, vl_size height,
01840                      double step, double sigma,
01841                      double sigmaI, double alpha)
01842 {
01843   float factor = (float) pow(sigma/step, 4.0) ;
01844   vl_index k ;
01845 
01846   float * LxLx ;
01847   float * LyLy ;
01848   float * LxLy ;
01849 
01850   LxLx = vl_malloc(sizeof(float) * width * height) ;
01851   LyLy = vl_malloc(sizeof(float) * width * height) ;
01852   LxLy = vl_malloc(sizeof(float) * width * height) ;
01853 
01854   vl_imgradient_f (LxLx, LyLy, 1, width, image, width, height, width) ;
01855 
01856   for (k = 0 ; k < (signed)(width * height) ; ++k) {
01857     float dx = LxLx[k] ;
01858     float dy = LyLy[k] ;
01859     LxLx[k] = dx*dx ;
01860     LyLy[k] = dy*dy ;
01861     LxLy[k] = dx*dy ;
01862   }
01863 
01864   vl_imsmooth_f(LxLx, width, LxLx, width, height, width,
01865                 sigmaI / step, sigmaI / step) ;
01866 
01867   vl_imsmooth_f(LyLy, width, LyLy, width, height, width,
01868                 sigmaI / step, sigmaI / step) ;
01869 
01870   vl_imsmooth_f(LxLy, width, LxLy, width, height, width,
01871                 sigmaI / step, sigmaI / step) ;
01872 
01873   for (k = 0 ; k < (signed)(width * height) ; ++k) {
01874     float a = LxLx[k] ;
01875     float b = LyLy[k] ;
01876     float c = LxLy[k] ;
01877 
01878     float determinant = a * b - c * c ;
01879     float trace = a + b ;
01880 
01881     harris[k] = factor * (determinant - alpha * (trace * trace)) ;
01882   }
01883 
01884   vl_free(LxLy) ;
01885   vl_free(LyLy) ;
01886   vl_free(LxLx) ;
01887 }
01888 
01897 static void
01898 _vl_dog_response (float * dog,
01899                   float const * level1,
01900                   float const * level2,
01901                   vl_size width, vl_size height)
01902 {
01903   vl_index k ;
01904   for (k = 0 ; k < (signed)(width*height) ; ++k) {
01905     dog[k] = level2[k] - level1[k] ;
01906   }
01907 }
01908 
01909 /* ---------------------------------------------------------------- */
01910 /*                                                  Detect features */
01911 /* ---------------------------------------------------------------- */
01912 
01920 void
01921 vl_covdet_detect (VlCovDet * self)
01922 {
01923   VlScaleSpaceGeometry geom = vl_scalespace_get_geometry(self->gss) ;
01924   VlScaleSpaceGeometry cgeom ;
01925   float * levelxx = NULL ;
01926   float * levelyy = NULL ;
01927   float * levelxy = NULL ;
01928   vl_index o, s ;
01929 
01930   assert (self) ;
01931   assert (self->gss) ;
01932 
01933   /* clear previous detections if any */
01934   self->numFeatures = 0 ;
01935 
01936   /* prepare buffers ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
01937   cgeom = geom ;
01938   if (self->method == VL_COVDET_METHOD_DOG) {
01939     cgeom.octaveLastSubdivision -= 1 ;
01940   }
01941   if (!self->css ||
01942       !vl_scalespacegeometry_is_equal(cgeom,
01943                                       vl_scalespace_get_geometry(self->css)))
01944   {
01945     if (self->css) vl_scalespace_delete(self->css) ;
01946     self->css = vl_scalespace_new_with_geometry(cgeom) ;
01947   }
01948   if (self->method == VL_COVDET_METHOD_HARRIS_LAPLACE ||
01949       self->method == VL_COVDET_METHOD_MULTISCALE_HARRIS) {
01950     VlScaleSpaceOctaveGeometry oct = vl_scalespace_get_octave_geometry(self->gss, geom.firstOctave) ;
01951     levelxx = vl_malloc(oct.width * oct.height * sizeof(float)) ;
01952     levelyy = vl_malloc(oct.width * oct.height * sizeof(float)) ;
01953     levelxy = vl_malloc(oct.width * oct.height * sizeof(float)) ;
01954   }
01955 
01956   /* compute cornerness ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
01957   for (o = cgeom.firstOctave ; o <= cgeom.lastOctave ; ++o) {
01958     VlScaleSpaceOctaveGeometry oct = vl_scalespace_get_octave_geometry(self->css, o) ;
01959 
01960     for (s = cgeom.octaveFirstSubdivision ; s <= cgeom.octaveLastSubdivision ; ++s) {
01961       float * level = vl_scalespace_get_level(self->gss, o, s) ;
01962       float * clevel = vl_scalespace_get_level(self->css, o, s) ;
01963       double sigma = vl_scalespace_get_level_sigma(self->css, o, s) ;
01964       switch (self->method) {
01965         case VL_COVDET_METHOD_DOG:
01966           _vl_dog_response(clevel,
01967                            vl_scalespace_get_level(self->gss, o, s + 1),
01968                            level,
01969                            oct.width, oct.height) ;
01970           break ;
01971 
01972         case VL_COVDET_METHOD_HARRIS_LAPLACE:
01973         case VL_COVDET_METHOD_MULTISCALE_HARRIS:
01974           _vl_harris_response(clevel,
01975                               level, oct.width, oct.height, oct.step,
01976                               sigma, 1.4 * sigma, 0.05) ;
01977           break ;
01978 
01979         case VL_COVDET_METHOD_HESSIAN:
01980         case VL_COVDET_METHOD_HESSIAN_LAPLACE:
01981         case VL_COVDET_METHOD_MULTISCALE_HESSIAN:
01982           _vl_det_hessian_response(clevel, level, oct.width, oct.height, oct.step, sigma) ;
01983           break ;
01984 
01985         default:
01986           assert(0) ;
01987       }
01988     }
01989   }
01990 
01991   /* find and refine local maxima ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
01992   {
01993     vl_index * extrema = NULL ;
01994     vl_size extremaBufferSize = 0 ;
01995     vl_size numExtrema ;
01996     vl_size index ;
01997     for (o = cgeom.firstOctave ; o <= cgeom.lastOctave ; ++o) {
01998       VlScaleSpaceOctaveGeometry octgeom = vl_scalespace_get_octave_geometry(self->css, o) ;
01999       double step = octgeom.step ;
02000       vl_size width = octgeom.width ;
02001       vl_size height = octgeom.height ;
02002       vl_size depth = cgeom.octaveLastSubdivision - cgeom.octaveFirstSubdivision + 1 ;
02003 
02004       switch (self->method) {
02005         case VL_COVDET_METHOD_DOG:
02006         case VL_COVDET_METHOD_HESSIAN:
02007         {
02008           /* scale-space extrema */
02009           float const * octave =
02010           vl_scalespace_get_level(self->css, o, cgeom.octaveFirstSubdivision) ;
02011           numExtrema = vl_find_local_extrema_3(&extrema, &extremaBufferSize,
02012                                                octave, width, height, depth,
02013                                                0.8 * self->peakThreshold);
02014           for (index = 0 ; index < numExtrema ; ++index) {
02015             VlCovDetExtremum3 refined ;
02016             VlCovDetFeature feature ;
02017             vl_bool ok ;
02018             memset(&feature, 0, sizeof(feature)) ;
02019             ok = vl_refine_local_extreum_3(&refined,
02020                                            octave, width, height, depth,
02021                                            extrema[3*index+0],
02022                                            extrema[3*index+1],
02023                                            extrema[3*index+2]) ;
02024             ok &= fabs(refined.peakScore) > self->peakThreshold ;
02025             ok &= refined.edgeScore < self->edgeThreshold ;
02026             if (ok) {
02027               double sigma = cgeom.baseScale *
02028               pow(2.0, o + (refined.z + cgeom.octaveFirstSubdivision)
02029                   / cgeom.octaveResolution) ;
02030               feature.frame.x = refined.x * step ;
02031               feature.frame.y = refined.y * step ;
02032               feature.frame.a11 = sigma ;
02033               feature.frame.a12 = 0.0 ;
02034               feature.frame.a21 = 0.0 ;
02035               feature.frame.a22 = sigma ;
02036               feature.peakScore = refined.peakScore ;
02037               feature.edgeScore = refined.edgeScore ;
02038               vl_covdet_append_feature(self, &feature) ;
02039             }
02040           }
02041           break ;
02042         }
02043 
02044         default:
02045         {
02046           for (s = cgeom.octaveFirstSubdivision ; s < cgeom.octaveLastSubdivision ; ++s) {
02047             /* space extrema */
02048             float const * level = vl_scalespace_get_level(self->css,o,s) ;
02049             numExtrema = vl_find_local_extrema_2(&extrema, &extremaBufferSize,
02050                                                  level,
02051                                                  width, height,
02052                                                  0.8 * self->peakThreshold);
02053             for (index = 0 ; index < numExtrema ; ++index) {
02054               VlCovDetExtremum2 refined ;
02055               VlCovDetFeature feature ;
02056               vl_bool ok ;
02057               memset(&feature, 0, sizeof(feature)) ;
02058               ok = vl_refine_local_extreum_2(&refined,
02059                                              level, width, height,
02060                                              extrema[2*index+0],
02061                                              extrema[2*index+1]);
02062               ok &= fabs(refined.peakScore) > self->peakThreshold ;
02063               ok &= refined.edgeScore < self->edgeThreshold ;
02064               if (ok) {
02065                 double sigma = cgeom.baseScale *
02066                 pow(2.0, o + (double)s / cgeom.octaveResolution) ;
02067                 feature.frame.x = refined.x * step ;
02068                 feature.frame.y = refined.y * step ;
02069                 feature.frame.a11 = sigma ;
02070                 feature.frame.a12 = 0.0 ;
02071                 feature.frame.a21 = 0.0 ;
02072                 feature.frame.a22 = sigma ;
02073                 feature.peakScore = refined.peakScore ;
02074                 feature.edgeScore = refined.edgeScore ;
02075                 vl_covdet_append_feature(self, &feature) ;
02076               }
02077             }
02078           }
02079           break ;
02080         }
02081       }
02082     } /* next octave */
02083 
02084     if (extrema) { vl_free(extrema) ; extrema = 0 ; }
02085   }
02086 
02087   /* Laplacian scale selection for certain methods */
02088   switch (self->method) {
02089     case VL_COVDET_METHOD_HARRIS_LAPLACE :
02090     case VL_COVDET_METHOD_HESSIAN_LAPLACE :
02091       vl_covdet_extract_laplacian_scales (self) ;
02092       break ;
02093     default:
02094       break ;
02095   }
02096 
02097   if (self->nonExtremaSuppression) {
02098     vl_index i, j ;
02099     double tol = self->nonExtremaSuppression ;
02100     self->numNonExtremaSuppressed = 0 ;
02101     for (i = 0 ; i < (signed)self->numFeatures ; ++i) {
02102       double x = self->features[i].frame.x ;
02103       double y = self->features[i].frame.y ;
02104       double sigma = self->features[i].frame.a11 ;
02105       double score = self->features[i].peakScore ;
02106 
02107       for (j = 0 ; j < (signed)self->numFeatures ; ++j) {
02108         double dx_ = self->features[j].frame.x - x ;
02109         double dy_ = self->features[j].frame.y - y ;
02110         double sigma_ = self->features[j].frame.a11 ;
02111         double score_ = self->features[j].peakScore ;
02112         if (score_ == 0) continue ;
02113         if (sigma < (1+tol) * sigma_ &&
02114             sigma_ < (1+tol) * sigma &&
02115             vl_abs_d(dx_) < tol * sigma &&
02116             vl_abs_d(dy_) < tol * sigma &&
02117             vl_abs_d(score) > vl_abs_d(score_)) {
02118           self->features[j].peakScore = 0 ;
02119           self->numNonExtremaSuppressed ++ ;
02120         }
02121       }
02122     }
02123     j = 0 ;
02124     for (i = 0 ; i < (signed)self->numFeatures ; ++i) {
02125       VlCovDetFeature feature = self->features[i] ;
02126       if (self->features[i].peakScore != 0) {
02127         self->features[j++] = feature ;
02128       }
02129     }
02130     self->numFeatures = j ;
02131   }
02132 
02133   if (levelxx) vl_free(levelxx) ;
02134   if (levelyy) vl_free(levelyy) ;
02135   if (levelxy) vl_free(levelxy) ;
02136 }
02137 
02138 /* ---------------------------------------------------------------- */
02139 /*                                                  Extract patches */
02140 /* ---------------------------------------------------------------- */
02141 
02157 vl_bool
02158 vl_covdet_extract_patch_helper (VlCovDet * self,
02159                                 double * sigma1,
02160                                 double * sigma2,
02161                                 float * patch,
02162                                 vl_size resolution,
02163                                 double extent,
02164                                 double sigma,
02165                                 double A_ [4],
02166                                 double T_ [2],
02167                                 double d1, double d2)
02168 {
02169   vl_index o, s ;
02170   double factor ;
02171   double sigma_ ;
02172   float const * level ;
02173   vl_size width, height ;
02174   double step ;
02175 
02176   double A [4] = {A_[0], A_[1], A_[2], A_[3]} ;
02177   double T [2] = {T_[0], T_[1]} ;
02178 
02179   VlScaleSpaceGeometry geom = vl_scalespace_get_geometry(self->gss) ;
02180   VlScaleSpaceOctaveGeometry oct ;
02181 
02182   /* Starting from a pre-smoothed image at scale sigma_
02183      because of the mapping A the resulting smoothing in
02184      the warped patch is S, where
02185 
02186         sigma_^2 I = A S A',
02187 
02188         S = sigma_^2 inv(A) inv(A)' = sigma_^2 V D^-2 V',
02189 
02190         A = U D V'.
02191 
02192      Thus we rotate A by V to obtain an axis-aligned smoothing:
02193 
02194         A = U*D,
02195 
02196         S = sigma_^2 D^-2.
02197 
02198      Then we search the scale-space for the best sigma_ such
02199      that the target smoothing is approximated from below:
02200 
02201         max sigma_(o,s) :    simga_(o,s) factor <= sigma,
02202         factor = max{abs(D11), abs(D22)}.
02203    */
02204 
02205 
02206   /*
02207    Determine the best level (o,s) such that sigma_(o,s) factor <= sigma.
02208    This can be obtained by scanning octaves from smallest to largest
02209    and stopping when no level in the octave satisfies the relation.
02210 
02211    Given the range of octave availables, do the best you can.
02212    */
02213 
02214   factor = 1.0 / VL_MIN(d1, d2) ;
02215 
02216   for (o = geom.firstOctave + 1 ; o <= geom.lastOctave ; ++o) {
02217     s = vl_floor_d(vl_log2_d(sigma / (factor * geom.baseScale)) - o) ;
02218     s = VL_MAX(s, geom.octaveFirstSubdivision) ;
02219     s = VL_MIN(s, geom.octaveLastSubdivision) ;
02220     sigma_ = geom.baseScale * pow(2.0, o + (double)s / geom.octaveResolution) ;
02221     /*VL_PRINTF(".. %d D=%g %g; sigma_=%g factor*sigma_=%g\n", o, d1, d2, sigma_, factor* sigma_) ;*/
02222     if (factor * sigma_ > sigma) {
02223       o -- ;
02224       break ;
02225     }
02226   }
02227   o = VL_MIN(o, geom.lastOctave) ;
02228   s = vl_floor_d(vl_log2_d(sigma / (factor * geom.baseScale)) - o) ;
02229   s = VL_MAX(s, geom.octaveFirstSubdivision) ;
02230   s = VL_MIN(s, geom.octaveLastSubdivision) ;
02231   sigma_ = geom.baseScale * pow(2.0, o + (double)s / geom.octaveResolution) ;
02232   if (sigma1) *sigma1 = sigma_ / d1 ;
02233   if (sigma2) *sigma2 = sigma_ / d2 ;
02234 
02235   /*VL_PRINTF("%d %d %g %g %g %g\n", o, s, factor, sigma_, factor * sigma_, sigma) ;*/
02236 
02237   /*
02238    Now the scale space level to be used for this warping has been
02239    determined.
02240 
02241    If the patch is partially or completely out of the image boundary,
02242    create a padded copy of the required region first.
02243    */
02244 
02245   level = vl_scalespace_get_level(self->gss, o, s) ;
02246   oct = vl_scalespace_get_octave_geometry(self->gss, o) ;
02247   width = oct.width ;
02248   height = oct.height ;
02249   step = oct.step ;
02250 
02251   A[0] /= step ;
02252   A[1] /= step ;
02253   A[2] /= step ;
02254   A[3] /= step ;
02255   T[0] /= step ;
02256   T[1] /= step ;
02257 
02258   {
02259     /*
02260      Warp the patch domain [x0hat,y0hat,x1hat,y1hat] to the image domain/
02261      Obtain a box [x0,y0,x1,y1] enclosing that wrapped box, and then
02262      an integer vertexes version [x0i, y0i, x1i, y1i], making room
02263      for one pixel at the boundary to simplify bilinear interpolation
02264      later on.
02265      */
02266     vl_index x0i, y0i, x1i, y1i ;
02267     double x0 = +VL_INFINITY_D ;
02268     double x1 = -VL_INFINITY_D ;
02269     double y0 = +VL_INFINITY_D ;
02270     double y1 = -VL_INFINITY_D ;
02271     double boxx [4] = {extent, extent, -extent, -extent} ;
02272     double boxy [4] = {-extent, extent, extent, -extent} ;
02273     int i ;
02274     for (i = 0 ; i < 4 ; ++i) {
02275       double x = A[0] * boxx[i] + A[2] * boxy[i] + T[0] ;
02276       double y = A[1] * boxx[i] + A[3] * boxy[i] + T[1] ;
02277       x0 = VL_MIN(x0, x) ;
02278       x1 = VL_MAX(x1, x) ;
02279       y0 = VL_MIN(y0, y) ;
02280       y1 = VL_MAX(y1, y) ;
02281     }
02282 
02283     /* Leave one pixel border for bilinear interpolation. */
02284     x0i = floor(x0) - 1 ;
02285     y0i = floor(y0) - 1 ;
02286     x1i = ceil(x1) + 1 ;
02287     y1i = ceil(y1) + 1 ;
02288 
02289     /*
02290      If the box [x0i,y0i,x1i,y1i] is not fully contained in the
02291      image domain, then create a copy of this region by padding
02292      the image. The image is extended by continuity.
02293      */
02294 
02295     if (x0i < 0 || x1i > (signed)width-1 ||
02296         y0i < 0 || y1i > (signed)height-1) {
02297       vl_index xi, yi ;
02298 
02299       /* compute the amount of l,r,t,b padding needed to complete the patch */
02300       vl_index padx0 = VL_MAX(0, - x0i) ;
02301       vl_index pady0 = VL_MAX(0, - y0i) ;
02302       vl_index padx1 = VL_MAX(0, x1i - ((signed)width - 1)) ;
02303       vl_index pady1 = VL_MAX(0, y1i - ((signed)height - 1)) ;
02304 
02305       /* make enough room for the patch */
02306       vl_index patchWidth = x1i - x0i + 1 ;
02307       vl_index patchHeight = y1i - y0i + 1 ;
02308       vl_size patchBufferSize = patchWidth * patchHeight * sizeof(float) ;
02309       if (patchBufferSize > self->patchBufferSize) {
02310         int err = _vl_resize_buffer((void**)&self->patch, &self->patchBufferSize, patchBufferSize) ;
02311         if (err) return vl_set_last_error(VL_ERR_ALLOC, NULL) ;
02312       }
02313 
02314       if (pady0 < patchHeight - pady1) {
02315         /* start by filling the central horizontal band */
02316         for (yi = y0i + pady0 ; yi < y0i + patchHeight - pady1 ; ++ yi) {
02317           float *dst = self->patch + (yi - y0i) * patchWidth ;
02318           float const *src = level + yi * width + VL_MIN(VL_MAX(0, x0i),(signed)width-1) ;
02319           for (xi = x0i ; xi < x0i + padx0 ; ++xi) *dst++ = *src ;
02320           for ( ; xi < x0i + patchWidth - padx1 - 2 ; ++xi) *dst++ = *src++ ;
02321           for ( ; xi < x0i + patchWidth ; ++xi) *dst++ = *src ;
02322         }
02323         /* now extend the central band up and down */
02324         for (yi = 0 ; yi < pady0 ; ++yi) {
02325           memcpy(self->patch + yi * patchWidth,
02326                  self->patch + pady0 * patchWidth,
02327                  patchWidth * sizeof(float)) ;
02328         }
02329         for (yi = patchHeight - pady1 ; yi < patchHeight ; ++yi) {
02330           memcpy(self->patch + yi * patchWidth,
02331                  self->patch + (patchHeight - pady1 - 1) * patchWidth,
02332                  patchWidth * sizeof(float)) ;
02333         }
02334       } else {
02335         /* should be handled better! */
02336         memset(self->patch, 0, self->patchBufferSize) ;
02337       }
02338 #if 0
02339       {
02340         char name [200] ;
02341         snprintf(name, 200, "/tmp/%20.0f-ext.pgm", 1e10*vl_get_cpu_time()) ;
02342         vl_pgm_write_f(name, patch, patchWidth, patchWidth) ;
02343       }
02344 #endif
02345 
02346       level = self->patch ;
02347       width = patchWidth ;
02348       height = patchHeight ;
02349       T[0] -= x0i ;
02350       T[1] -= y0i ;
02351     }
02352   }
02353 
02354   /*
02355    Resample by using bilinear interpolation.
02356    */
02357   {
02358     float * pt = patch ;
02359     double yhat = -extent ;
02360     vl_index xxi ;
02361     vl_index yyi ;
02362     double stephat = extent / resolution ;
02363 
02364     for (yyi = 0 ; yyi < 2 * (signed)resolution + 1 ; ++yyi) {
02365       double xhat = -extent ;
02366       double rx = A[2] * yhat + T[0] ;
02367       double ry = A[3] * yhat + T[1] ;
02368       for (xxi = 0 ; xxi < 2 * (signed)resolution + 1 ; ++xxi) {
02369         double x = A[0] * xhat + rx ;
02370         double y = A[1] * xhat + ry ;
02371         vl_index xi = vl_floor_d(x) ;
02372         vl_index yi = vl_floor_d(y) ;
02373         double i00 = level[yi * width + xi] ;
02374         double i10 = level[yi * width + xi + 1] ;
02375         double i01 = level[(yi + 1) * width + xi] ;
02376         double i11 = level[(yi + 1) * width + xi + 1] ;
02377         double wx = x - xi ;
02378         double wy = y - yi ;
02379 
02380         assert(xi >= 0 && xi <= (signed)width - 1) ;
02381         assert(yi >= 0 && yi <= (signed)height - 1) ;
02382 
02383         *pt++ =
02384         (1.0 - wy) * ((1.0 - wx) * i00 + wx * i10) +
02385         wy * ((1.0 - wx) * i01 + wx * i11) ;
02386 
02387         xhat += stephat ;
02388       }
02389       yhat += stephat ;
02390     }
02391   }
02392 #if 0
02393     {
02394       char name [200] ;
02395       snprintf(name, 200, "/tmp/%20.0f.pgm", 1e10*vl_get_cpu_time()) ;
02396       vl_pgm_write_f(name, patch, 2*resolution+1, 2*resolution+1) ;
02397     }
02398 #endif
02399   return VL_ERR_OK ;
02400 }
02401 
02422 vl_bool
02423 vl_covdet_extract_patch_for_frame (VlCovDet * self,
02424                                    float * patch,
02425                                    vl_size resolution,
02426                                    double extent,
02427                                    double sigma,
02428                                    VlFrameOrientedEllipse frame)
02429 {
02430   double A[2*2] = {frame.a11, frame.a21, frame.a12, frame.a22} ;
02431   double T[2] = {frame.x, frame.y} ;
02432   double D[4], U[4], V[4] ;
02433 
02434   vl_svd2(D, U, V, A) ;
02435 
02436   return vl_covdet_extract_patch_helper
02437   (self, NULL, NULL, patch, resolution, extent, sigma, A, T, D[0], D[3]) ;
02438 }
02439 
02440 /* ---------------------------------------------------------------- */
02441 /*                                                     Affine shape */
02442 /* ---------------------------------------------------------------- */
02443 
02454 int
02455 vl_covdet_extract_affine_shape_for_frame (VlCovDet * self,
02456                                           VlFrameOrientedEllipse * adapted,
02457                                           VlFrameOrientedEllipse frame)
02458 {
02459   vl_index iter = 0 ;
02460 
02461   double A [2*2] = {frame.a11, frame.a21, frame.a12, frame.a22} ;
02462   double T [2] = {frame.x, frame.y} ;
02463   double U [2*2] ;
02464   double V [2*2] ;
02465   double D [2*2] ;
02466   double M [2*2] ;
02467   double P [2*2] ;
02468   double P_ [2*2] ;
02469   double Q [2*2] ;
02470   double sigma1, sigma2 ;
02471   double sigmaD = VL_COVDET_AA_RELATIVE_DERIVATIVE_SIGMA ;
02472   double factor ;
02473   double anisotropy ;
02474   double referenceScale ;
02475   vl_size const resolution = VL_COVDET_AA_PATCH_RESOLUTION ;
02476   vl_size const side = 2*VL_COVDET_AA_PATCH_RESOLUTION + 1 ;
02477   double const extent = VL_COVDET_AA_PATCH_EXTENT ;
02478 
02479 
02480   *adapted = frame ;
02481 
02482   while (1) {
02483     double lxx = 0, lxy = 0, lyy = 0 ;
02484     vl_index k ;
02485     int err ;
02486 
02487     /* A = U D V' */
02488     vl_svd2(D, U, V, A) ;
02489     anisotropy = VL_MAX(D[0]/D[3], D[3]/D[0]) ;
02490 
02491     /* VL_PRINTF("anisot: %g\n", anisotropy); */
02492 
02493     if (anisotropy > VL_COVDET_AA_MAX_ANISOTROPY) {
02494       /* diverged, give up with current solution */
02495       break ;
02496     }
02497 
02498     /* make sure that the smallest singluar value stays fixed
02499        after the first iteration */
02500     if (iter == 0) {
02501       referenceScale = VL_MIN(D[0], D[3]) ;
02502       factor = 1.0 ;
02503     } else {
02504       factor = referenceScale / VL_MIN(D[0],D[3]) ;
02505     }
02506 
02507     D[0] *= factor ;
02508     D[3] *= factor ;
02509 
02510     A[0] = U[0] * D[0] ;
02511     A[1] = U[1] * D[0] ;
02512     A[2] = U[2] * D[3] ;
02513     A[3] = U[3] * D[3] ;
02514 
02515     adapted->a11 = A[0] ;
02516     adapted->a21 = A[1] ;
02517     adapted->a12 = A[2] ;
02518     adapted->a22 = A[3] ;
02519 
02520     if (++iter >= VL_COVDET_AA_MAX_NUM_ITERATIONS) break ;
02521 
02522     err = vl_covdet_extract_patch_helper(self,
02523                                          &sigma1, &sigma2,
02524                                          self->aaPatch,
02525                                          resolution,
02526                                          extent,
02527                                          sigmaD,
02528                                          A, T, D[0], D[3]) ;
02529     if (err) return err ;
02530 
02531     if (self->aaAccurateSmoothing ) {
02532       double deltaSigma1 = sqrt(VL_MAX(sigmaD*sigmaD - sigma1*sigma1,0)) ;
02533       double deltaSigma2 = sqrt(VL_MAX(sigmaD*sigmaD - sigma2*sigma2,0)) ;
02534       double stephat = extent / resolution ;
02535       vl_imsmooth_f(self->aaPatch, side,
02536                     self->aaPatch, side, side, side,
02537                     deltaSigma1 / stephat, deltaSigma2 / stephat) ;
02538     }
02539 
02540     /* compute second moment matrix */
02541     vl_imgradient_f (self->aaPatchX, self->aaPatchY, 1, side,
02542                      self->aaPatch, side, side, side) ;
02543 
02544     for (k = 0 ; k < (signed)(side*side) ; ++k) {
02545       double lx = self->aaPatchX[k] ;
02546       double ly = self->aaPatchY[k] ;
02547       lxx += lx * lx * self->aaMask[k] ;
02548       lyy += ly * ly * self->aaMask[k] ;
02549       lxy += lx * ly * self->aaMask[k] ;
02550     }
02551     M[0] = lxx ;
02552     M[1] = lxy ;
02553     M[2] = lxy ;
02554     M[3] = lyy ;
02555 
02556     if (lxx == 0 || lyy == 0) {
02557       *adapted = frame ;
02558       break ;
02559     }
02560 
02561     /* decompose M = P * Q * P' */
02562     vl_svd2 (Q, P, P_, M) ;
02563 
02564     /*
02565      Setting A <- A * dA results in M to change approximatively as
02566 
02567      M --> dA'  M dA = dA' P Q P dA
02568 
02569      To make this proportional to the identity, we set
02570 
02571      dA ~= P Q^1/2
02572 
02573      we also make it so the smallest singular value of A is unchanged.
02574      */
02575 
02576     if (Q[3]/Q[0] < VL_COVDET_AA_CONVERGENCE_THRESHOLD &&
02577         Q[0]/Q[3] < VL_COVDET_AA_CONVERGENCE_THRESHOLD) {
02578       break ;
02579     }
02580 
02581     {
02582       double Ap [4] ;
02583       double q0 = sqrt(Q[0]) ;
02584       double q1 = sqrt(Q[3]) ;
02585       Ap[0] = (A[0] * P[0] + A[2] * P[1]) / q0 ;
02586       Ap[1] = (A[1] * P[0] + A[3] * P[1]) / q0 ;
02587       Ap[2] = (A[0] * P[2] + A[2] * P[3]) / q1 ;
02588       Ap[3] = (A[1] * P[2] + A[3] * P[3]) / q1 ;
02589       memcpy(A,Ap,4*sizeof(double)) ;
02590     }
02591 
02592   } /* next iteration */
02593 
02594   /*
02595    Make upright.
02596 
02597    Shape adaptation does not estimate rotation. This is fixed by default
02598    so that a selected axis is not rotated at all (usually this is the
02599    vertical axis for upright features). To do so, the frame is rotated
02600    as follows.
02601    */
02602   {
02603     double A [2*2] = {adapted->a11, adapted->a21, adapted->a12, adapted->a22} ;
02604     double ref [2] ;
02605     double ref_ [2] ;
02606     double angle ;
02607     double angle_ ;
02608     double dangle ;
02609     double r1, r2 ;
02610 
02611     if (self->transposed) {
02612       /* up is the x axis */
02613       ref[0] = 1 ;
02614       ref[1] = 0 ;
02615     } else {
02616       /* up is the y axis */
02617       ref[0] = 0 ;
02618       ref[1] = 1 ;
02619     }
02620 
02621     vl_solve_linear_system_2 (ref_, A, ref) ;
02622     angle = atan2(ref[1], ref[0]) ;
02623     angle_ = atan2(ref_[1], ref_[0]) ;
02624     dangle = angle_ - angle ;
02625     r1 = cos(dangle) ;
02626     r2 = sin(dangle) ;
02627     adapted->a11 = + A[0] * r1 + A[2] * r2 ;
02628     adapted->a21 = + A[1] * r1 + A[3] * r2 ;
02629     adapted->a12 = - A[0] * r2 + A[2] * r1 ;
02630     adapted->a22 = - A[1] * r2 + A[3] * r1 ;
02631   }
02632 
02633   return VL_ERR_OK ;
02634 }
02635 
02643 void
02644 vl_covdet_extract_affine_shape (VlCovDet * self)
02645 {
02646   vl_index i, j = 0 ;
02647   vl_size numFeatures = vl_covdet_get_num_features(self) ;
02648   VlCovDetFeature * feature = vl_covdet_get_features(self);
02649   for (i = 0 ; i < (signed)numFeatures ; ++i) {
02650     int status ;
02651     VlFrameOrientedEllipse adapted ;
02652     status = vl_covdet_extract_affine_shape_for_frame(self, &adapted, feature[i].frame) ;
02653     if (status == VL_ERR_OK) {
02654       feature[j] = feature[i] ;
02655       feature[j].frame = adapted ;
02656       ++ j ;
02657     }
02658   }
02659   self->numFeatures = j ;
02660 }
02661 
02662 /* ---------------------------------------------------------------- */
02663 /*                                                      Orientation */
02664 /* ---------------------------------------------------------------- */
02665 
02666 static int
02667 _vl_covdet_compare_orientations_descending (void const * a_,
02668                                             void const * b_)
02669 {
02670   VlCovDetFeatureOrientation const * a = a_ ;
02671   VlCovDetFeatureOrientation const * b = b_ ;
02672   if (a->score > b->score) return -1 ;
02673   if (a->score < b->score) return +1 ;
02674   return 0 ;
02675 }
02676 
02689 VlCovDetFeatureOrientation *
02690 vl_covdet_extract_orientations_for_frame (VlCovDet * self,
02691                                           vl_size * numOrientations,
02692                                           VlFrameOrientedEllipse frame)
02693 {
02694   int err ;
02695   vl_index k, i ;
02696   vl_index iter ;
02697 
02698   double extent = VL_COVDET_AA_PATCH_EXTENT ;
02699   vl_size resolution = VL_COVDET_AA_PATCH_RESOLUTION ;
02700   vl_size side = 2 * resolution + 1  ;
02701 
02702   vl_size const numBins = VL_COVDET_OR_NUM_ORIENTATION_HISTOGAM_BINS ;
02703   double hist [VL_COVDET_OR_NUM_ORIENTATION_HISTOGAM_BINS] ;
02704   double const binExtent = 2 * VL_PI / VL_COVDET_OR_NUM_ORIENTATION_HISTOGAM_BINS ;
02705   double const peakRelativeSize = VL_COVDET_OR_ADDITIONAL_PEAKS_RELATIVE_SIZE ;
02706   double maxPeakValue ;
02707 
02708   double A [2*2] = {frame.a11, frame.a21, frame.a12, frame.a22} ;
02709   double T [2] = {frame.x, frame.y} ;
02710   double U [2*2] ;
02711   double V [2*2] ;
02712   double D [2*2] ;
02713   double sigma1, sigma2 ;
02714   double sigmaD = 1.0 ;
02715   double theta0 ;
02716 
02717   assert(self);
02718   assert(numOrientations) ;
02719 
02720   /*
02721    The goal is to estimate a rotation R(theta) such that the patch given
02722    by the transformation A R(theta) has the strongest average
02723    gradient pointing right (or down for transposed conventions).
02724 
02725    To compensate for tha anisotropic smoothing due to warping,
02726    A is decomposed as A = U D V' and the patch is warped by
02727    U D only, meaning that the matrix R_(theta) will be estimated instead,
02728    where:
02729 
02730       A R(theta) = U D V' R(theta) = U D R_(theta)
02731 
02732    such that R(theta) = V R(theta). That is an extra rotation of
02733    theta0 = atan2(V(2,1),V(1,1)).
02734    */
02735 
02736   /* axis aligned anisotropic smoothing for easier compensation */
02737   vl_svd2(D, U, V, A) ;
02738 
02739   A[0] = U[0] * D[0] ;
02740   A[1] = U[1] * D[0] ;
02741   A[2] = U[2] * D[3] ;
02742   A[3] = U[3] * D[3] ;
02743 
02744   theta0 = atan2(V[1],V[0]) ;
02745 
02746   err = vl_covdet_extract_patch_helper(self,
02747                                        &sigma1, &sigma2,
02748                                        self->aaPatch,
02749                                        resolution,
02750                                        extent,
02751                                        sigmaD,
02752                                        A, T, D[0], D[3]) ;
02753 
02754   if (err) {
02755     *numOrientations = 0 ;
02756     return NULL ;
02757   }
02758 
02759   if (1) {
02760     double deltaSigma1 = sqrt(VL_MAX(sigmaD*sigmaD - sigma1*sigma1,0)) ;
02761     double deltaSigma2 = sqrt(VL_MAX(sigmaD*sigmaD - sigma2*sigma2,0)) ;
02762     double stephat = extent / resolution ;
02763     vl_imsmooth_f(self->aaPatch, side,
02764                   self->aaPatch, side, side, side,
02765                   deltaSigma1 / stephat, deltaSigma2 / stephat) ;
02766   }
02767 
02768   /* histogram of oriented gradients */
02769   vl_imgradient_polar_f (self->aaPatchX, self->aaPatchY, 1, side,
02770                          self->aaPatch, side, side, side) ;
02771 
02772   memset (hist, 0, sizeof(double) * numBins) ;
02773 
02774   for (k = 0 ; k < (signed)(side*side) ; ++k) {
02775     double modulus = self->aaPatchX[k] ;
02776     double angle = self->aaPatchY[k] ;
02777     double weight = self->aaMask[k] ;
02778 
02779     double x = angle / binExtent ;
02780     vl_index bin = vl_floor_d(x) ;
02781     double w2 = x - bin ;
02782     double w1 = 1.0 - w2 ;
02783 
02784     hist[(bin + numBins) % numBins] += w1 * (modulus * weight) ;
02785     hist[(bin + numBins + 1) % numBins] += w2 * (modulus * weight) ;
02786   }
02787 
02788   /* smooth histogram */
02789   for (iter = 0; iter < 6; iter ++) {
02790     double prev = hist [numBins - 1] ;
02791     double first = hist [0] ;
02792     vl_index i ;
02793     for (i = 0; i < (signed)numBins - 1; ++i) {
02794       double curr = (prev + hist[i] + hist[(i + 1) % numBins]) / 3.0 ;
02795       prev = hist[i] ;
02796       hist[i] = curr ;
02797     }
02798     hist[i] = (prev + hist[i] + first) / 3.0 ;
02799   }
02800 
02801   /* find the histogram maximum */
02802   maxPeakValue = 0 ;
02803   for (i = 0 ; i < (signed)numBins ; ++i) {
02804     maxPeakValue = VL_MAX (maxPeakValue, hist[i]) ;
02805   }
02806 
02807   /* find peaks within 80% from max */
02808   *numOrientations = 0 ;
02809   for(i = 0 ; i < (signed)numBins ; ++i) {
02810     double h0 = hist [i] ;
02811     double hm = hist [(i - 1 + numBins) % numBins] ;
02812     double hp = hist [(i + 1 + numBins) % numBins] ;
02813 
02814     /* is this a peak? */
02815     if (h0 > peakRelativeSize * maxPeakValue && h0 > hm && h0 > hp) {
02816       /* quadratic interpolation */
02817       double di = - 0.5 * (hp - hm) / (hp + hm - 2 * h0) ;
02818       double th = binExtent * (i + di) + theta0 ;
02819       if (self->transposed) {
02820         /* the axis to the right is y, measure orientations from this */
02821         th = th - VL_PI/2 ;
02822       }
02823       self->orientations[*numOrientations].angle = th ;
02824       self->orientations[*numOrientations].score = h0 ;
02825       *numOrientations += 1 ;
02826       //VL_PRINTF("%d %g\n", *numOrientations, th) ;
02827 
02828       if (*numOrientations >= VL_COVDET_MAX_NUM_ORIENTATIONS) break ;
02829     }
02830   }
02831 
02832   /* sort the orientations by decreasing scores */
02833   qsort(self->orientations,
02834         *numOrientations,
02835         sizeof(VlCovDetFeatureOrientation),
02836         _vl_covdet_compare_orientations_descending) ;
02837 
02838   return self->orientations ;
02839 }
02840 
02849 void
02850 vl_covdet_extract_orientations (VlCovDet * self)
02851 {
02852   vl_index i, j  ;
02853   vl_size numFeatures = vl_covdet_get_num_features(self) ;
02854   for (i = 0 ; i < (signed)numFeatures ; ++i) {
02855     vl_size numOrientations ;
02856     VlCovDetFeature feature = self->features[i] ;
02857     VlCovDetFeatureOrientation* orientations =
02858     vl_covdet_extract_orientations_for_frame(self, &numOrientations, feature.frame) ;
02859 
02860     for (j = 0 ; j < (signed)numOrientations ; ++j) {
02861       double A [2*2] = {
02862         feature.frame.a11,
02863         feature.frame.a21,
02864         feature.frame.a12,
02865         feature.frame.a22} ;
02866       double r1 = cos(orientations[j].angle) ;
02867       double r2 = sin(orientations[j].angle) ;
02868       VlCovDetFeature * oriented ;
02869 
02870       if (j == 0) {
02871         oriented = & self->features[i] ;
02872       } else {
02873         vl_covdet_append_feature(self, &feature) ;
02874         oriented = & self->features[self->numFeatures -1] ;
02875       }
02876 
02877       oriented->orientationScore = orientations[j].score ;
02878       oriented->frame.a11 = + A[0] * r1 + A[2] * r2 ;
02879       oriented->frame.a21 = + A[1] * r1 + A[3] * r2 ;
02880       oriented->frame.a12 = - A[0] * r2 + A[2] * r1 ;
02881       oriented->frame.a22 = - A[1] * r2 + A[3] * r1 ;
02882     }
02883   }
02884 }
02885 
02886 /* ---------------------------------------------------------------- */
02887 /*                                                 Laplacian scales */
02888 /* ---------------------------------------------------------------- */
02889 
02899 VlCovDetFeatureLaplacianScale *
02900 vl_covdet_extract_laplacian_scales_for_frame (VlCovDet * self,
02901                                               vl_size * numScales,
02902                                               VlFrameOrientedEllipse frame)
02903 {
02904   /*
02905    We try to explore one octave, with the nominal detection scale 1.0
02906    (in the patch reference frame) in the middle. Thus the goal is to sample
02907    the response of the tr-Laplacian operator at logarithmically
02908    spaced scales in 1/sqrt(2), sqrt(2).
02909 
02910    To this end, the patch is warped with a smoothing of at most
02911    sigmaImage = 1 / sqrt(2) (beginning of the scale), sampled at
02912    roughly twice the Nyquist frequency (so step = 1 / (2*sqrt(2))).
02913    This maes it possible to approximate the Laplacian operator at
02914    that scale by simple finite differences.
02915 
02916    */
02917   int err ;
02918   double const sigmaImage = 1.0 / sqrt(2.0) ;
02919   double const step = 0.5 * sigmaImage ;
02920   double actualSigmaImage ;
02921   vl_size const resolution = VL_COVDET_LAP_PATCH_RESOLUTION ;
02922   vl_size const num = 2 * resolution + 1 ;
02923   double extent = step * resolution ;
02924   double scores [VL_COVDET_LAP_NUM_LEVELS] ;
02925   double factor = 1.0 ;
02926   float const * pt ;
02927   vl_index k ;
02928 
02929   double A[2*2] = {frame.a11, frame.a21, frame.a12, frame.a22} ;
02930   double T[2] = {frame.x, frame.y} ;
02931   double D[4], U[4], V[4] ;
02932   double sigma1, sigma2 ;
02933 
02934   assert(self) ;
02935   assert(numScales) ;
02936 
02937   *numScales = 0 ;
02938 
02939   vl_svd2(D, U, V, A) ;
02940 
02941   err = vl_covdet_extract_patch_helper
02942   (self, &sigma1, &sigma2, self->lapPatch, resolution, extent, sigmaImage, A, T, D[0], D[3]) ;
02943   if (err) return NULL ;
02944 
02945   /* the actual smoothing after warping is never the target one */
02946   if (sigma1 == sigma2) {
02947     actualSigmaImage = sigma1 ;
02948   } else {
02949     /* here we could compensate */
02950     actualSigmaImage = sqrt(sigma1*sigma2) ;
02951   }
02952 
02953   /* now multiply by the bank of Laplacians */
02954   pt = self->laplacians ;
02955   for (k = 0 ; k < VL_COVDET_LAP_NUM_LEVELS ; ++k) {
02956     vl_index q ;
02957     double score = 0 ;
02958     double sigmaLap = pow(2.0, -0.5 + (double)k / (VL_COVDET_LAP_NUM_LEVELS - 1)) ;
02959     /* note that the sqrt argument cannot be negative since by construction
02960      sigmaLap >= sigmaImage */
02961     sigmaLap = sqrt(sigmaLap*sigmaLap
02962                     - sigmaImage*sigmaImage
02963                     + actualSigmaImage*actualSigmaImage) ;
02964 
02965     for (q = 0 ; q < (signed)(num * num) ; ++q) {
02966       score += (*pt++) * self->lapPatch[q] ;
02967     }
02968     scores[k] = score * sigmaLap * sigmaLap ;
02969   }
02970 
02971   /* find and interpolate maxima */
02972   for (k = 1 ; k < VL_COVDET_LAP_NUM_LEVELS - 1 ; ++k) {
02973     double a = scores[k-1] ;
02974     double b = scores[k] ;
02975     double c = scores[k+1] ;
02976     double t = self->lapPeakThreshold ;
02977 
02978     if ((((b > a) && (b > c)) || ((b < a) && (b < c))) && (vl_abs_d(b) >= t)) {
02979       double dk = - 0.5 * (c - a) / (c + a - 2 * b) ;
02980       double s = k + dk ;
02981       double sigmaLap = pow(2.0, -0.5 + s / (VL_COVDET_LAP_NUM_LEVELS - 1)) ;
02982       double scale ;
02983       sigmaLap = sqrt(sigmaLap*sigmaLap
02984                       - sigmaImage*sigmaImage
02985                       + actualSigmaImage*actualSigmaImage) ;
02986       scale = sigmaLap / 1.0 ;
02987       /*
02988        VL_PRINTF("** k:%d, s:%f, sigmaLapFilter:%f, sigmaLap%f, scale:%f (%f %f %f)\n",
02989        k,s,sigmaLapFilter,sigmaLap,scale,a,b,c) ;
02990        */
02991       if (*numScales < VL_COVDET_MAX_NUM_LAPLACIAN_SCALES) {
02992         self->scales[*numScales].scale = scale * factor ;
02993         self->scales[*numScales].score = b + 0.5 * (c - a) * dk ;
02994         *numScales += 1 ;
02995       }
02996     }
02997   }
02998   return self->scales ;
02999 }
03000 
03008 void
03009 vl_covdet_extract_laplacian_scales (VlCovDet * self)
03010 {
03011   vl_index i, j  ;
03012   vl_bool dropFeaturesWithoutScale = VL_TRUE ;
03013   vl_size numFeatures = vl_covdet_get_num_features(self) ;
03014   memset(self->numFeaturesWithNumScales, 0,
03015          sizeof(self->numFeaturesWithNumScales)) ;
03016 
03017   for (i = 0 ; i < (signed)numFeatures ; ++i) {
03018     vl_size numScales ;
03019     VlCovDetFeature feature = self->features[i] ;
03020     VlCovDetFeatureLaplacianScale const * scales =
03021     vl_covdet_extract_laplacian_scales_for_frame(self, &numScales, feature.frame) ;
03022 
03023     self->numFeaturesWithNumScales[numScales] ++ ;
03024 
03025     if (numScales == 0 && dropFeaturesWithoutScale) {
03026       self->features[i].peakScore = 0 ;
03027     }
03028 
03029     for (j = 0 ; j < (signed)numScales ; ++j) {
03030       VlCovDetFeature * scaled ;
03031 
03032       if (j == 0) {
03033         scaled = & self->features[i] ;
03034       } else {
03035         vl_covdet_append_feature(self, &feature) ;
03036         scaled = & self->features[self->numFeatures -1] ;
03037       }
03038 
03039       scaled->laplacianScaleScore = scales[j].score ;
03040       scaled->frame.a11 *= scales[j].scale ;
03041       scaled->frame.a21 *= scales[j].scale ;
03042       scaled->frame.a12 *= scales[j].scale ;
03043       scaled->frame.a22 *= scales[j].scale ;
03044     }
03045   }
03046   if (dropFeaturesWithoutScale) {
03047     j = 0 ;
03048     for (i = 0 ; i < (signed)self->numFeatures ; ++i) {
03049       VlCovDetFeature feature = self->features[i] ;
03050       if (feature.peakScore) {
03051         self->features[j++] = feature ;
03052       }
03053     }
03054     self->numFeatures = j ;
03055   }
03056 
03057 }
03058 
03059 /* ---------------------------------------------------------------- */
03060 /*                       Checking that features are inside an image */
03061 /* ---------------------------------------------------------------- */
03062 
03063 vl_bool
03064 _vl_covdet_check_frame_inside (VlCovDet * self, VlFrameOrientedEllipse frame, double margin)
03065 {
03066   double extent = margin ;
03067   double A [2*2] = {frame.a11, frame.a21, frame.a12, frame.a22} ;
03068   double T[2] = {frame.x, frame.y} ;
03069   double x0 = +VL_INFINITY_D ;
03070   double x1 = -VL_INFINITY_D ;
03071   double y0 = +VL_INFINITY_D ;
03072   double y1 = -VL_INFINITY_D ;
03073   double boxx [4] = {extent, extent, -extent, -extent} ;
03074   double boxy [4] = {-extent, extent, extent, -extent} ;
03075   VlScaleSpaceGeometry geom = vl_scalespace_get_geometry(self->gss) ;
03076   int i ;
03077   for (i = 0 ; i < 4 ; ++i) {
03078     double x = A[0] * boxx[i] + A[2] * boxy[i] + T[0] ;
03079     double y = A[1] * boxx[i] + A[3] * boxy[i] + T[1] ;
03080     x0 = VL_MIN(x0, x) ;
03081     x1 = VL_MAX(x1, x) ;
03082     y0 = VL_MIN(y0, y) ;
03083     y1 = VL_MAX(y1, y) ;
03084   }
03085 
03086   return
03087   0 <= x0 && x1 <= geom.width-1 &&
03088   0 <= y0 && y1 <= geom.height-1 ;
03089 }
03090 
03107 void
03108 vl_covdet_drop_features_outside (VlCovDet * self, double margin)
03109 {
03110   vl_index i, j = 0 ;
03111   vl_size numFeatures = vl_covdet_get_num_features(self) ;
03112   for (i = 0 ; i < (signed)numFeatures ; ++i) {
03113     vl_bool inside =
03114     _vl_covdet_check_frame_inside (self, self->features[i].frame, margin) ;
03115     if (inside) {
03116       self->features[j] = self->features[i] ;
03117       ++j ;
03118     }
03119   }
03120   self->numFeatures = j ;
03121 }
03122 
03123 /* ---------------------------------------------------------------- */
03124 /*                                              Setters and getters */
03125 /* ---------------------------------------------------------------- */
03126 
03127 /* ---------------------------------------------------------------- */
03132 vl_bool
03133 vl_covdet_get_transposed (VlCovDet const  * self)
03134 {
03135   return self->transposed ;
03136 }
03137 
03142 void
03143 vl_covdet_set_transposed (VlCovDet * self, vl_bool t)
03144 {
03145   self->transposed = t ;
03146 }
03147 
03148 /* ---------------------------------------------------------------- */
03153 double
03154 vl_covdet_get_edge_threshold (VlCovDet const * self)
03155 {
03156   return self->edgeThreshold ;
03157 }
03158 
03165 void
03166 vl_covdet_set_edge_threshold (VlCovDet * self, double edgeThreshold)
03167 {
03168   assert(edgeThreshold >= 0) ;
03169   self->edgeThreshold = edgeThreshold ;
03170 }
03171 
03172 /* ---------------------------------------------------------------- */
03177 double
03178 vl_covdet_get_peak_threshold (VlCovDet const * self)
03179 {
03180   return self->peakThreshold ;
03181 }
03182 
03189 void
03190 vl_covdet_set_peak_threshold (VlCovDet * self, double peakThreshold)
03191 {
03192   assert(peakThreshold >= 0) ;
03193   self->peakThreshold = peakThreshold ;
03194 }
03195 
03196 /* ---------------------------------------------------------------- */
03204 double
03205 vl_covdet_get_laplacian_peak_threshold (VlCovDet const * self)
03206 {
03207   return self->lapPeakThreshold ;
03208 }
03209 
03216 void
03217 vl_covdet_set_laplacian_peak_threshold (VlCovDet * self, double peakThreshold)
03218 {
03219   assert(peakThreshold >= 0) ;
03220   self->lapPeakThreshold = peakThreshold ;
03221 }
03222 
03223 /* ---------------------------------------------------------------- */
03228 vl_index
03229 vl_covdet_get_first_octave (VlCovDet const * self)
03230 {
03231   return self->firstOctave ;
03232 }
03233 
03240 void
03241 vl_covdet_set_first_octave (VlCovDet * self, vl_index o)
03242 {
03243   self->firstOctave = o ;
03244   vl_covdet_reset(self) ;
03245 }
03246 
03247 /* ---------------------------------------------------------------- */
03253 vl_size
03254 vl_covdet_get_octave_resolution (VlCovDet const * self)
03255 {
03256   return self->octaveResolution ;
03257 }
03258 
03266 void
03267 vl_covdet_set_octave_resolution (VlCovDet * self, vl_size r)
03268 {
03269   self->octaveResolution = r ;
03270   vl_covdet_reset(self) ;
03271 }
03272 
03273 /* ---------------------------------------------------------------- */
03279 vl_bool
03280 vl_covdet_get_aa_accurate_smoothing (VlCovDet const * self)
03281 {
03282   return self->aaAccurateSmoothing ;
03283 }
03284 
03290 void
03291 vl_covdet_set_aa_accurate_smoothing (VlCovDet * self, vl_bool x)
03292 {
03293   self->aaAccurateSmoothing = x ;
03294 }
03295 
03296 /* ---------------------------------------------------------------- */
03302 double
03303 vl_covdet_get_non_extrema_suppression_threshold (VlCovDet const * self)
03304 {
03305   return self->nonExtremaSuppression ;
03306 }
03307 
03313 void
03314 vl_covdet_set_non_extrema_suppression_threshold (VlCovDet * self, double x)
03315 {
03316   self->nonExtremaSuppression = x ;
03317 }
03318 
03324 vl_size
03325 vl_covdet_get_num_non_extrema_suppressed (VlCovDet const * self)
03326 {
03327   return self->numNonExtremaSuppressed ;
03328 }
03329 
03330 
03331 /* ---------------------------------------------------------------- */
03335 vl_size
03336 vl_covdet_get_num_features (VlCovDet const * self)
03337 {
03338   return self->numFeatures ;
03339 }
03340 
03344 void *
03345 vl_covdet_get_features (VlCovDet * self)
03346 {
03347   return self->features ;
03348 }
03349 
03357 VlScaleSpace *
03358 vl_covdet_get_gss (VlCovDet const * self)
03359 {
03360   return self->gss ;
03361 }
03362 
03370 VlScaleSpace *
03371 vl_covdet_get_css (VlCovDet const * self)
03372 {
03373   return self->css ;
03374 }
03375 
03386 vl_size const *
03387 vl_covdet_get_laplacian_scales_statistics (VlCovDet const * self,
03388                                            vl_size * numScales)
03389 {
03390   *numScales = VL_COVDET_MAX_NUM_LAPLACIAN_SCALES ;
03391   return self->numFeaturesWithNumScales ;
03392 }


libvlfeat
Author(s): Andrea Vedaldi
autogenerated on Thu Jun 6 2019 20:25:51