mser.c
Go to the documentation of this file.
00001 
00006 /*
00007 Copyright (C) 2007-13 Andrea Vedaldi and Brian Fulkerson.
00008 All rights reserved.
00009 
00010 This file is part of the VLFeat library and is made available under
00011 the terms of the BSD license (see the COPYING file).
00012 */
00013 
00220 #include "mser.h"
00221 #include<stdlib.h>
00222 #include<string.h>
00223 #include<assert.h>
00224 
00236 VL_INLINE void
00237 adv(int ndims, int const *dims, int *subs)
00238 {
00239   int d = 0 ;
00240   while(d < ndims) {
00241     if( ++subs[d]  < dims[d] ) return ;
00242     subs[d++] = 0 ;
00243   }
00244 }
00245 
00261 VL_INLINE vl_uint
00262 climb (VlMserReg* r, vl_uint idx)
00263 {
00264 
00265   vl_uint prev_idx = idx ;
00266   vl_uint next_idx ;
00267   vl_uint root_idx ;
00268 
00269   /* move towards root to find it */
00270   while (1) {
00271 
00272     /* next jump to the root */
00273     next_idx = r [idx] .shortcut ;
00274 
00275     /* recycle shortcut to remember how we came here */
00276     r [idx] .shortcut = prev_idx ;
00277 
00278     /* stop if the root is found */
00279     if( next_idx == idx ) break ;
00280 
00281     /* next guy */
00282     prev_idx = idx ;
00283     idx      = next_idx ;
00284   }
00285 
00286   root_idx = idx ;
00287 
00288   /* move backward to update shortcuts */
00289   while (1) {
00290 
00291     /* get previously visited one */
00292     prev_idx = r [idx] .shortcut ;
00293 
00294     /* update shortcut to point to the new root */
00295     r [idx] .shortcut = root_idx ;
00296 
00297     /* stop if the first visited node is reached */
00298     if( prev_idx == idx ) break ;
00299 
00300     /* next guy */
00301     idx = prev_idx ;
00302   }
00303 
00304   return root_idx ;
00305 }
00306 
00317 VL_EXPORT
00318 VlMserFilt*
00319 vl_mser_new (int ndims, int const* dims)
00320 {
00321   VlMserFilt* f ;
00322   int *strides, k ;
00323 
00324   f = vl_calloc (sizeof(VlMserFilt), 1) ;
00325 
00326   f-> ndims   = ndims ;
00327   f-> dims    = vl_malloc (sizeof(int) * ndims) ;
00328   f-> subs    = vl_malloc (sizeof(int) * ndims) ;
00329   f-> dsubs   = vl_malloc (sizeof(int) * ndims) ;
00330   f-> strides = vl_malloc (sizeof(int) * ndims) ;
00331 
00332   /* shortcuts */
00333   strides = f-> strides ;
00334 
00335   /* copy dims to f->dims */
00336   for(k = 0 ; k < ndims ; ++k) {
00337     f-> dims [k] = dims [k] ;
00338   }
00339 
00340   /* compute strides to move into the N-dimensional image array */
00341   strides [0] = 1 ;
00342   for(k = 1 ; k < ndims ; ++k) {
00343     strides [k] = strides [k-1] * dims [k-1] ;
00344   }
00345 
00346   /* total number of pixels */
00347   f-> nel = strides [ndims-1] * dims [ndims-1] ;
00348 
00349   /* dof of ellipsoids */
00350   f-> dof = ndims * (ndims + 1) / 2 + ndims ;
00351 
00352   /* more buffers */
00353   f-> perm   = vl_malloc (sizeof(vl_uint)   * f-> nel) ;
00354   f-> joins  = vl_malloc (sizeof(vl_uint)   * f-> nel) ;
00355   f-> r      = vl_malloc (sizeof(VlMserReg) * f-> nel) ;
00356 
00357   f-> er     = 0 ;
00358   f-> rer    = 0 ;
00359   f-> mer    = 0 ;
00360   f-> rmer   = 0 ;
00361   f-> ell    = 0 ;
00362   f-> rell   = 0 ;
00363 
00364   /* other parameters */
00365   f-> delta         = 5 ;
00366   f-> max_area      = 0.75 ;
00367   f-> min_area      = 3.0 / f-> nel ;
00368   f-> max_variation = 0.25 ;
00369   f-> min_diversity = 0.2 ;
00370 
00371   return f ;
00372 }
00373 
00381 VL_EXPORT
00382 void
00383 vl_mser_delete (VlMserFilt* f)
00384 {
00385   if(f) {
00386     if(f-> acc   )  vl_free( f-> acc    ) ;
00387     if(f-> ell   )  vl_free( f-> ell    ) ;
00388 
00389     if(f-> er    )  vl_free( f-> er     ) ;
00390     if(f-> r     )  vl_free( f-> r      ) ;
00391     if(f-> joins )  vl_free( f-> joins  ) ;
00392     if(f-> perm  )  vl_free( f-> perm   ) ;
00393 
00394     if(f-> strides) vl_free( f-> strides) ;
00395     if(f-> dsubs  ) vl_free( f-> dsubs  ) ;
00396     if(f-> subs   ) vl_free( f-> subs   ) ;
00397     if(f-> dims   ) vl_free( f-> dims   ) ;
00398 
00399     if(f-> mer    ) vl_free( f-> mer    ) ;
00400     vl_free (f) ;
00401   }
00402 }
00403 
00404 
00417 VL_EXPORT
00418 void
00419 vl_mser_process (VlMserFilt* f, vl_mser_pix const* im)
00420 {
00421   /* shortcuts */
00422   vl_uint        nel     = f-> nel  ;
00423   vl_uint       *perm    = f-> perm ;
00424   vl_uint       *joins   = f-> joins ;
00425   int            ndims   = f-> ndims ;
00426   int           *dims    = f-> dims ;
00427   int           *subs    = f-> subs ;
00428   int           *dsubs   = f-> dsubs ;
00429   int           *strides = f-> strides ;
00430   VlMserReg     *r       = f-> r ;
00431   VlMserExtrReg *er      = f-> er ;
00432   vl_uint       *mer     = f-> mer ;
00433   int            delta   = f-> delta ;
00434 
00435   int njoins = 0 ;
00436   int ner    = 0 ;
00437   int nmer   = 0 ;
00438   int nbig   = 0 ;
00439   int nsmall = 0 ;
00440   int nbad   = 0 ;
00441   int ndup   = 0 ;
00442 
00443   int i, j, k ;
00444 
00445   /* delete any previosuly computed ellipsoid */
00446   f-> nell = 0 ;
00447 
00448   /* -----------------------------------------------------------------
00449    *                                          Sort pixels by intensity
00450    * -------------------------------------------------------------- */
00451 
00452   {
00453     vl_uint buckets [ VL_MSER_PIX_MAXVAL ] ;
00454 
00455     /* clear buckets */
00456     memset (buckets, 0, sizeof(vl_uint) * VL_MSER_PIX_MAXVAL ) ;
00457 
00458     /* compute bucket size (how many pixels for each intensity
00459        value) */
00460     for(i = 0 ; i < (int) nel ; ++i) {
00461       vl_mser_pix v = im [i] ;
00462       ++ buckets [v] ;
00463     }
00464 
00465     /* cumulatively add bucket sizes */
00466     for(i = 1 ; i < VL_MSER_PIX_MAXVAL ; ++i) {
00467       buckets [i] += buckets [i-1] ;
00468     }
00469 
00470     /* empty buckets computing pixel ordering */
00471     for(i = nel ; i >= 1 ; ) {
00472       vl_mser_pix v = im [ --i ] ;
00473       vl_uint j = -- buckets [v] ;
00474       perm [j] = i ;
00475     }
00476   }
00477 
00478   /* initialize the forest with all void nodes */
00479   for(i = 0 ; i < (int) nel ; ++i) {
00480     r [i] .parent = VL_MSER_VOID_NODE ;
00481   }
00482 
00483   /* -----------------------------------------------------------------
00484    *                        Compute regions and count extremal regions
00485    * -------------------------------------------------------------- */
00486   /*
00487      In the following:
00488 
00489      idx    : index of the current pixel
00490      val    : intensity of the current pixel
00491      r_idx  : index of the root of the current pixel
00492      n_idx  : index of the neighbors of the current pixel
00493      nr_idx : index of the root of the neighbor of the current pixel
00494 
00495   */
00496 
00497   /* process each pixel by increasing intensity */
00498   for(i = 0 ; i < (int) nel ; ++i) {
00499 
00500     /* pop next node xi */
00501     vl_uint     idx = perm [i] ;
00502     vl_mser_pix val = im [idx] ;
00503     vl_uint     r_idx ;
00504 
00505     /* add the pixel to the forest as a root for now */
00506     r [idx] .parent   = idx ;
00507     r [idx] .shortcut = idx ;
00508     r [idx] .area     = 1 ;
00509     r [idx] .height   = 1 ;
00510 
00511     r_idx = idx ;
00512 
00513     /* convert the index IDX into the subscript SUBS; also initialize
00514        DSUBS to (-1,-1,...,-1) */
00515     {
00516       vl_uint temp = idx ;
00517       for(k = ndims - 1 ; k >= 0 ; --k) {
00518         dsubs [k] = -1 ;
00519         subs  [k] = temp / strides [k] ;
00520         temp      = temp % strides [k] ;
00521       }
00522     }
00523 
00524     /* examine the neighbors of the current pixel */
00525     while (1) {
00526       vl_uint n_idx = 0 ;
00527       vl_bool good = 1 ;
00528 
00529       /*
00530          Compute the neighbor subscript as NSUBS+SUB, the
00531          corresponding neighbor index NINDEX and check that the
00532          neighbor is within the image domain.
00533       */
00534       for(k = 0 ; k < ndims && good ; ++k) {
00535         int temp  = dsubs [k] + subs [k] ;
00536         good     &= (0 <= temp) && (temp < dims [k]) ;
00537         n_idx    += temp * strides [k] ;
00538       }
00539 
00540       /*
00541          The neighbor should be processed if the following conditions
00542          are met:
00543 
00544          1. The neighbor is within image boundaries.
00545 
00546          2. The neighbor is indeed different from the current node
00547             (the opposite happens when DSUB=(0,0,...,0)).
00548 
00549          3. The neighbor is already in the forest, meaning that it has
00550             already been processed.
00551       */
00552       if (good &&
00553           n_idx != idx &&
00554           r [n_idx] .parent != VL_MSER_VOID_NODE ) {
00555 
00556         vl_mser_pix nr_val = 0 ;
00557         vl_uint     nr_idx = 0 ;
00558         int         hgt   = r [ r_idx] .height ;
00559         int         n_hgt = r [nr_idx] .height ;
00560 
00561         /*
00562           Now we join the two subtrees rooted at
00563 
00564            R_IDX = ROOT(  IDX)
00565           NR_IDX = ROOT(N_IDX).
00566 
00567           Note that R_IDX = ROOT(IDX) might change as we process more
00568           neighbors, so we need keep updating it.
00569         */
00570 
00571          r_idx = climb(r,   idx) ;
00572         nr_idx = climb(r, n_idx) ;
00573 
00574         /*
00575           At this point we have three possibilities:
00576 
00577           (A) ROOT(IDX) == ROOT(NR_IDX). In this case the two trees
00578               have already been joined and we do not do anything.
00579 
00580           (B) I(ROOT(IDX)) == I(ROOT(NR_IDX)). In this case the pixel
00581               IDX is extending an extremal region with the same
00582               intensity value. Since ROOT(NR_IDX) will NOT be an
00583               extremal region of the full image, ROOT(IDX) can be
00584               safely added as children of ROOT(NR_IDX) if this
00585               reduces the height according to the union rank
00586               heuristic.
00587 
00588           (C) I(ROOT(IDX)) > I(ROOT(NR_IDX)). In this case the pixel
00589               IDX is starting a new extremal region. Thus ROOT(NR_IDX)
00590               WILL be an extremal region of the final image and the
00591               only possibility is to add ROOT(NR_IDX) as children of
00592               ROOT(IDX), which becomes parent.
00593         */
00594 
00595         if( r_idx != nr_idx ) { /* skip if (A) */
00596 
00597           nr_val = im [nr_idx] ;
00598 
00599           if( nr_val == val && hgt < n_hgt ) {
00600 
00601             /* ROOT(IDX) becomes the child */
00602             r [r_idx]  .parent   = nr_idx ;
00603             r [r_idx]  .shortcut = nr_idx ;
00604             r [nr_idx] .area    += r [r_idx] .area ;
00605             r [nr_idx] .height   = VL_MAX(n_hgt, hgt+1) ;
00606 
00607             joins [njoins++] = r_idx ;
00608 
00609           } else {
00610 
00611             /* cases ROOT(IDX) becomes the parent */
00612             r [nr_idx] .parent   = r_idx ;
00613             r [nr_idx] .shortcut = r_idx ;
00614             r [r_idx]  .area    += r [nr_idx] .area ;
00615             r [r_idx]  .height   = VL_MAX(hgt, n_hgt + 1) ;
00616 
00617             joins [njoins++] = nr_idx ;
00618 
00619             /* count if extremal */
00620             if (nr_val != val) ++ ner ;
00621 
00622           } /* check b vs c */
00623         } /* check a vs b or c */
00624       } /* neighbor done */
00625 
00626       /* move to next neighbor */
00627       k = 0 ;
00628       while(++ dsubs [k] > 1) {
00629         dsubs [k++] = -1 ;
00630         if(k == ndims) goto done_all_neighbors ;
00631       }
00632     } /* next neighbor */
00633   done_all_neighbors : ;
00634   } /* next pixel */
00635 
00636   /* the last root is extremal too */
00637   ++ ner ;
00638 
00639   /* save back */
00640   f-> njoins = njoins ;
00641 
00642   f-> stats. num_extremal = ner ;
00643 
00644   /* -----------------------------------------------------------------
00645    *                                          Extract extremal regions
00646    * -------------------------------------------------------------- */
00647 
00648   /*
00649      Extremal regions are extracted and stored into the array ER.  The
00650      structure R is also updated so that .SHORTCUT indexes the
00651      corresponding extremal region if any (otherwise it is set to
00652      VOID).
00653   */
00654 
00655   /* make room */
00656   if (f-> rer < ner) {
00657     if (er) vl_free (er) ;
00658     f->er  = er = vl_malloc (sizeof(VlMserExtrReg) * ner) ;
00659     f->rer = ner ;
00660   } ;
00661 
00662   /* save back */
00663   f-> nmer = ner ;
00664 
00665   /* count again */
00666   ner = 0 ;
00667 
00668   /* scan all regions Xi */
00669   for(i = 0 ; i < (int) nel ; ++i) {
00670 
00671     /* pop next node xi */
00672     vl_uint     idx = perm [i] ;
00673 
00674     vl_mser_pix val   = im [idx] ;
00675     vl_uint     p_idx = r  [idx] .parent ;
00676     vl_mser_pix p_val = im [p_idx] ;
00677 
00678     /* is extremal ? */
00679     vl_bool is_extr = (p_val > val) || idx == p_idx ;
00680 
00681     if( is_extr ) {
00682 
00683       /* if so, add it */
00684       er [ner] .index      = idx ;
00685       er [ner] .parent     = ner ;
00686       er [ner] .value      = im [idx] ;
00687       er [ner] .area       = r  [idx] .area ;
00688 
00689       /* link this region to this extremal region */
00690       r [idx] .shortcut = ner ;
00691 
00692       /* increase count */
00693       ++ ner ;
00694     } else {
00695       /* link this region to void */
00696       r [idx] .shortcut =   VL_MSER_VOID_NODE ;
00697     }
00698   }
00699 
00700   /* -----------------------------------------------------------------
00701    *                                   Link extremal regions in a tree
00702    * -------------------------------------------------------------- */
00703 
00704   for(i = 0 ; i < ner ; ++i) {
00705 
00706     vl_uint idx = er [i] .index ;
00707 
00708     do {
00709       idx = r[idx] .parent ;
00710     } while (r[idx] .shortcut == VL_MSER_VOID_NODE) ;
00711 
00712     er[i] .parent   = r[idx] .shortcut ;
00713     er[i] .shortcut = i ;
00714   }
00715 
00716   /* -----------------------------------------------------------------
00717    *                            Compute variability of +DELTA branches
00718    * -------------------------------------------------------------- */
00719   /* For each extremal region Xi of value VAL we look for the biggest
00720    * parent that has value not greater than VAL+DELTA. This is dubbed
00721    * `top parent'. */
00722 
00723   for(i = 0 ; i < ner ; ++i) {
00724 
00725     /* Xj is the current region the region and Xj are the parents */
00726     int     top_val = er [i] .value + delta ;
00727     int     top     = er [i] .shortcut ;
00728 
00729     /* examine all parents */
00730     while (1) {
00731       int next     = er [top]  .parent ;
00732       int next_val = er [next] .value ;
00733 
00734       /* Break if:
00735        * - there is no node above the top or
00736        * - the next node is above the top value.
00737        */
00738       if (next == top || next_val > top_val) break ;
00739 
00740       /* so next could be the top */
00741       top = next ;
00742     }
00743 
00744     /* calculate branch variation */
00745     {
00746       int area     = er [i  ] .area ;
00747       int area_top = er [top] .area ;
00748       er [i] .variation  = (float) (area_top - area) / area ;
00749       er [i] .max_stable = 1 ;
00750     }
00751 
00752     /* Optimization: since extremal regions are processed by
00753      * increasing intensity, all next extremal regions being processed
00754      * have value at least equal to the one of Xi. If any of them has
00755      * parent the parent of Xi (this comprises the parent itself), we
00756      * can safely skip most intermediate node along the branch and
00757      * skip directly to the top to start our search. */
00758     {
00759       int parent = er [i] .parent ;
00760       int curr   = er [parent] .shortcut ;
00761       er [parent] .shortcut =  VL_MAX (top, curr) ;
00762     }
00763   }
00764 
00765   /* -----------------------------------------------------------------
00766    *                                  Select maximally stable branches
00767    * -------------------------------------------------------------- */
00768 
00769   nmer = ner ;
00770   for(i = 0 ; i < ner ; ++i) {
00771     vl_uint    parent = er [i     ] .parent ;
00772     vl_mser_pix   val = er [i     ] .value ;
00773     float     var = er [i     ] .variation ;
00774     vl_mser_pix p_val = er [parent] .value ;
00775     float   p_var = er [parent] .variation ;
00776     vl_uint     loser ;
00777 
00778     /*
00779        Notice that R_parent = R_{l+1} only if p_val = val + 1. If not,
00780        this and the parent region coincide and there is nothing to do.
00781     */
00782     if(p_val > val + 1) continue ;
00783 
00784     /* decide which one to keep and put that in loser */
00785     if(var < p_var) loser = parent ; else loser = i ;
00786 
00787     /* make loser NON maximally stable */
00788     if(er [loser] .max_stable) {
00789       -- nmer ;
00790       er [loser] .max_stable = 0 ;
00791     }
00792   }
00793 
00794   f-> stats. num_unstable = ner - nmer ;
00795 
00796   /* -----------------------------------------------------------------
00797    *                                                 Further filtering
00798    * -------------------------------------------------------------- */
00799   /* It is critical for correct duplicate detection to remove regions
00800    * from the bottom (smallest one first).                          */
00801   {
00802     float max_area = (float) f-> max_area * nel ;
00803     float min_area = (float) f-> min_area * nel ;
00804     float max_var  = (float) f-> max_variation ;
00805     float min_div  = (float) f-> min_diversity ;
00806 
00807     /* scan all extremal regions (intensity value order) */
00808     for(i = ner-1 ; i >= 0L  ; --i) {
00809 
00810       /* process only maximally stable extremal regions */
00811       if (! er [i] .max_stable) continue ;
00812 
00813       if (er [i] .variation >= max_var ) { ++ nbad ;   goto remove ; }
00814       if (er [i] .area      >  max_area) { ++ nbig ;   goto remove ; }
00815       if (er [i] .area      <  min_area) { ++ nsmall ; goto remove ; }
00816 
00817       /*
00818        * Remove duplicates
00819        */
00820       if (min_div < 1.0) {
00821         vl_uint   parent = er [i] .parent ;
00822         int       area, p_area ;
00823         float div ;
00824 
00825         /* check all but the root mser */
00826         if((int) parent != i) {
00827 
00828           /* search for the maximally stable parent region */
00829           while(! er [parent] .max_stable) {
00830             vl_uint next = er [parent] .parent ;
00831             if(next == parent) break ;
00832             parent = next ;
00833           }
00834 
00835           /* Compare with the parent region; if the current and parent
00836            * regions are too similar, keep only the parent. */
00837           area    = er [i]      .area ;
00838           p_area  = er [parent] .area ;
00839           div     = (float) (p_area - area) / (float) p_area ;
00840 
00841           if (div < min_div) { ++ ndup ; goto remove ; }
00842         } /* remove dups end */
00843 
00844       }
00845       continue ;
00846     remove :
00847       er [i] .max_stable = 0 ;
00848       -- nmer ;
00849     } /* check next region */
00850 
00851     f-> stats .num_abs_unstable = nbad ;
00852     f-> stats .num_too_big      = nbig ;
00853     f-> stats .num_too_small    = nsmall ;
00854     f-> stats .num_duplicates   = ndup ;
00855   }
00856   /* -----------------------------------------------------------------
00857    *                                                   Save the result
00858    * -------------------------------------------------------------- */
00859 
00860   /* make room */
00861   if (f-> rmer < nmer) {
00862     if (mer) vl_free (mer) ;
00863     f->mer  = mer = vl_malloc( sizeof(vl_uint) * nmer) ;
00864     f->rmer = nmer ;
00865   }
00866 
00867   /* save back */
00868   f-> nmer = nmer ;
00869 
00870   j = 0 ;
00871   for (i = 0 ; i < ner ; ++i) {
00872     if (er [i] .max_stable) mer [j++] = er [i] .index ;
00873   }
00874 }
00875 
00884 VL_EXPORT
00885 void
00886 vl_mser_ell_fit (VlMserFilt* f)
00887 {
00888   /* shortcuts */
00889   int                nel = f-> nel ;
00890   int                dof = f-> dof ;
00891   int              *dims = f-> dims ;
00892   int              ndims = f-> ndims ;
00893   int              *subs = f-> subs ;
00894   int             njoins = f-> njoins ;
00895   vl_uint         *joins = f-> joins ;
00896   VlMserReg           *r = f-> r ;
00897   vl_uint           *mer = f-> mer ;
00898   int               nmer = f-> nmer ;
00899   vl_mser_acc       *acc = f-> acc ;
00900   vl_mser_acc       *ell = f-> ell ;
00901 
00902   int d, index, i, j ;
00903 
00904   /* already fit ? */
00905   if (f->nell == f->nmer) return ;
00906 
00907   /* make room */
00908   if (f->rell < f->nmer) {
00909     if (f->ell) vl_free (f->ell) ;
00910     f->ell  = vl_malloc (sizeof(float) * f->nmer * f->dof) ;
00911     f->rell = f-> nmer ;
00912   }
00913 
00914   if (f->acc == 0) {
00915     f->acc = vl_malloc (sizeof(float) * f->nel) ;
00916   }
00917 
00918   acc = f-> acc ;
00919   ell = f-> ell ;
00920 
00921   /* -----------------------------------------------------------------
00922    *                                                 Integrate moments
00923    * -------------------------------------------------------------- */
00924 
00925   /* for each dof */
00926   for(d = 0 ; d < f->dof ; ++d) {
00927 
00928     /* start from the upper-left pixel (0,0,...,0) */
00929     memset (subs, 0, sizeof(int) * ndims) ;
00930 
00931     /* step 1: fill acc pretending that each region has only one pixel */
00932     if(d < ndims) {
00933       /* 1-order ................................................... */
00934 
00935       for(index = 0 ; index < nel ; ++ index) {
00936         acc [index] = subs [d] ;
00937         adv(ndims, dims, subs) ;
00938       }
00939     }
00940     else {
00941       /* 2-order ................................................... */
00942 
00943       /* map the dof d to a second order moment E[x_i x_j] */
00944       i = d - ndims ;
00945       j = 0 ;
00946       while(i > j) {
00947         i -= j + 1 ;
00948         j ++ ;
00949       }
00950       /* initialize acc with  x_i * x_j */
00951       for(index = 0 ; index < nel ; ++ index){
00952         acc [index] = subs [i] * subs [j] ;
00953         adv(ndims, dims, subs) ;
00954       }
00955     }
00956 
00957     /* step 2: integrate */
00958     for(i = 0 ; i < njoins ; ++i) {
00959       vl_uint index  = joins [i] ;
00960       vl_uint parent = r [index] .parent ;
00961       acc [parent] += acc [index] ;
00962     }
00963 
00964     /* step 3: save back to ellpises */
00965     for(i = 0 ; i < nmer ; ++i) {
00966       vl_uint idx = mer [i] ;
00967       ell [d + dof*i] = acc [idx] ;
00968     }
00969 
00970   }  /* next dof */
00971 
00972   /* -----------------------------------------------------------------
00973    *                                           Compute central moments
00974    * -------------------------------------------------------------- */
00975 
00976   for(index = 0 ; index < nmer ; ++index) {
00977     float  *pt  = ell + index * dof ;
00978     vl_uint    idx  = mer [index] ;
00979     float  area = r [idx] .area ;
00980 
00981     for(d = 0 ; d < dof ; ++d) {
00982 
00983       pt [d] /= area ;
00984 
00985       if(d >= ndims) {
00986         /* remove squared mean from moment to get variance */
00987         i = d - ndims ;
00988         j = 0 ;
00989         while(i > j) {
00990           i -= j + 1 ;
00991           j ++ ;
00992         }
00993         pt [d] -= pt [i] * pt [j] ;
00994       }
00995 
00996     }
00997   }
00998 
00999   /* save back */
01000   f-> nell = nmer ;
01001 }


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