Home | History | Annotate | Download | only in src
      1 /* Redistribution and use in source and binary forms, with or
      2  * without modification, are permitted provided that the following
      3  * conditions are met:
      4  * 	Redistributions of source code must retain the above
      5  * 	copyright notice, this list of conditions and the following
      6  * 	disclaimer.
      7  * 	Redistributions in binary form must reproduce the above
      8  * 	copyright notice, this list of conditions and the following
      9  * 	disclaimer in the documentation and/or other materials
     10  * 	provided with the distribution.
     11  * 	The name of Contributor may not be used to endorse or
     12  * 	promote products derived from this software without
     13  * 	specific prior written permission.
     14  *
     15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
     16  * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
     17  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     19  * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
     20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     22  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
     23  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
     25  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
     26  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
     27  * OF SUCH DAMAGE.
     28  * Copyright 2009, Liu Liu All rights reserved.
     29  *
     30  * OpenCV functions for MSER extraction
     31  *
     32  * 1. there are two different implementation of MSER, one for gray image, one for color image
     33  * 2. the gray image algorithm is taken from: Linear Time Maximally Stable Extremal Regions;
     34  *    the paper claims to be faster than union-find method;
     35  *    it actually get 1.5~2m/s on my centrino L7200 1.2GHz laptop.
     36  * 3. the color image algorithm is taken from: Maximally Stable Colour Regions for Recognition and Match;
     37  *    it should be much slower than gray image method ( 3~4 times );
     38  *    the chi_table.h file is taken directly from paper's source code which is distributed under GPL.
     39  * 4. though the name is *contours*, the result actually is a list of point set.
     40  */
     41 
     42 #include "precomp.hpp"
     43 #include "opencv2/imgproc/imgproc_c.h"
     44 #include <limits>
     45 
     46 namespace cv
     47 {
     48 
     49 using std::vector;
     50 
     51 class MSER_Impl : public MSER
     52 {
     53 public:
     54     struct Params
     55     {
     56         Params( int _delta=5, int _min_area=60, int _max_area=14400,
     57                    double _max_variation=0.25, double _min_diversity=.2,
     58                    int _max_evolution=200, double _area_threshold=1.01,
     59                    double _min_margin=0.003, int _edge_blur_size=5 )
     60         {
     61             delta = _delta;
     62             minArea = _min_area;
     63             maxArea = _max_area;
     64             maxVariation = _max_variation;
     65             minDiversity = _min_diversity;
     66             maxEvolution = _max_evolution;
     67             areaThreshold = _area_threshold;
     68             minMargin = _min_margin;
     69             edgeBlurSize = _edge_blur_size;
     70             pass2Only = false;
     71         }
     72 
     73         int delta;
     74         int minArea;
     75         int maxArea;
     76         double maxVariation;
     77         double minDiversity;
     78         bool pass2Only;
     79 
     80         int maxEvolution;
     81         double areaThreshold;
     82         double minMargin;
     83         int edgeBlurSize;
     84     };
     85 
     86     explicit MSER_Impl(const Params& _params) : params(_params) {}
     87 
     88     virtual ~MSER_Impl() {}
     89 
     90     void setDelta(int delta) { params.delta = delta; }
     91     int getDelta() const { return params.delta; }
     92 
     93     void setMinArea(int minArea) { params.minArea = minArea; }
     94     int getMinArea() const { return params.minArea; }
     95 
     96     void setMaxArea(int maxArea) { params.maxArea = maxArea; }
     97     int getMaxArea() const { return params.maxArea; }
     98 
     99     void setPass2Only(bool f) { params.pass2Only = f; }
    100     bool getPass2Only() const { return params.pass2Only; }
    101 
    102     enum { DIR_SHIFT = 29, NEXT_MASK = ((1<<DIR_SHIFT)-1)  };
    103 
    104     struct Pixel
    105     {
    106         Pixel() : val(0) {}
    107         Pixel(int _val) : val(_val) {}
    108 
    109         int getGray(const Pixel* ptr0, const uchar* imgptr0, int mask) const
    110         {
    111             return imgptr0[this - ptr0] ^ mask;
    112         }
    113         int getNext() const { return (val & NEXT_MASK); }
    114         void setNext(int next) { val = (val & ~NEXT_MASK) | next; }
    115 
    116         int getDir() const { return (int)((unsigned)val >> DIR_SHIFT); }
    117         void setDir(int dir) { val = (val & NEXT_MASK) | (dir << DIR_SHIFT); }
    118         bool isVisited() const { return (val & ~NEXT_MASK) != 0; }
    119 
    120         int val;
    121     };
    122     typedef int PPixel;
    123 
    124     struct WParams
    125     {
    126         Params p;
    127         vector<vector<Point> >* msers;
    128         vector<Rect>* bboxvec;
    129         Pixel* pix0;
    130         int step;
    131     };
    132 
    133     // the history of region grown
    134     struct CompHistory
    135     {
    136         CompHistory()
    137         {
    138             parent_ = child_ = next_ = 0;
    139             val = size = 0;
    140             var = -1.f;
    141             head = 0;
    142             checked = false;
    143         }
    144         void updateTree( WParams& wp, CompHistory** _h0, CompHistory** _h1, bool final )
    145         {
    146             if( var >= 0.f )
    147                 return;
    148             int delta = wp.p.delta;
    149 
    150             CompHistory* h0_ = 0, *h1_ = 0;
    151             CompHistory* c = child_;
    152             if( size >= wp.p.minArea )
    153             {
    154                 for( ; c != 0; c = c->next_ )
    155                 {
    156                     if( c->var < 0.f )
    157                         c->updateTree(wp, c == child_ ? &h0_ : 0, c == child_ ? &h1_ : 0, final);
    158                     if( c->var < 0.f )
    159                         return;
    160                 }
    161             }
    162 
    163             // find h0 and h1 such that:
    164             //    h0->val >= h->val - delta and (h0->parent == 0 or h0->parent->val < h->val - delta)
    165             //    h1->val <= h->val + delta and (h1->child == 0 or h1->child->val < h->val + delta)
    166             // then we will adjust h0 and h1 as h moves towards latest
    167             CompHistory* h0 = this, *h1 = h1_ && h1_->size > size ? h1_ : this;
    168             if( h0_ )
    169             {
    170                 for( h0 = h0_; h0 != this && h0->val < val - delta; h0 = h0->parent_ )
    171                     ;
    172             }
    173             else
    174             {
    175                 for( ; h0->child_ && h0->child_->val >= val - delta; h0 = h0->child_ )
    176                     ;
    177             }
    178 
    179             for( ; h1->parent_ && h1->parent_->val <= val + delta; h1 = h1->parent_ )
    180                 ;
    181 
    182             if( _h0 ) *_h0 = h0;
    183             if( _h1 ) *_h1 = h1;
    184 
    185             // when we do not well-defined ER(h->val + delta), we stop
    186             // the process of computing variances unless we are at the final step
    187             if( !final && !h1->parent_ && h1->val < val + delta )
    188                 return;
    189 
    190             var = (float)(h1->size - h0->size)/size;
    191             c = child_;
    192             for( ; c != 0; c = c->next_ )
    193                 c->checkAndCapture(wp);
    194             if( final && !parent_ )
    195                 checkAndCapture(wp);
    196         }
    197 
    198         void checkAndCapture( WParams& wp )
    199         {
    200             if( checked )
    201                 return;
    202             checked = true;
    203             if( size < wp.p.minArea || size > wp.p.maxArea || var < 0.f || var > wp.p.maxVariation )
    204                 return;
    205             if( child_ )
    206             {
    207                 CompHistory* c = child_;
    208                 for( ; c != 0; c = c->next_ )
    209                 {
    210                     if( c->var >= 0.f && var > c->var )
    211                         return;
    212                 }
    213             }
    214             if( parent_ && parent_->var >= 0.f && var >= parent_->var )
    215                 return;
    216             int xmin = INT_MAX, ymin = INT_MAX, xmax = INT_MIN, ymax = INT_MIN, j = 0;
    217             wp.msers->push_back(vector<Point>());
    218             vector<Point>& region = wp.msers->back();
    219             region.resize(size);
    220             const Pixel* pix0 = wp.pix0;
    221             int step = wp.step;
    222 
    223             for( PPixel pix = head; j < size; j++, pix = pix0[pix].getNext() )
    224             {
    225                 int y = pix/step;
    226                 int x = pix - y*step;
    227 
    228                 xmin = std::min(xmin, x);
    229                 xmax = std::max(xmax, x);
    230                 ymin = std::min(ymin, y);
    231                 ymax = std::max(ymax, y);
    232 
    233                 region[j] = Point(x, y);
    234             }
    235 
    236             wp.bboxvec->push_back(Rect(xmin, ymin, xmax - xmin + 1, ymax - ymin + 1));
    237         }
    238 
    239         CompHistory* child_;
    240         CompHistory* parent_;
    241         CompHistory* next_;
    242         int val;
    243         int size;
    244         float var;
    245         PPixel head;
    246         bool checked;
    247     };
    248 
    249     struct ConnectedComp
    250     {
    251         ConnectedComp()
    252         {
    253             init(0);
    254         }
    255 
    256         void init(int gray)
    257         {
    258             head = tail = 0;
    259             history = 0;
    260             size = 0;
    261             gray_level = gray;
    262         }
    263 
    264         // add history chunk to a connected component
    265         void growHistory( CompHistory*& hptr, WParams& wp, int new_gray_level, bool final, bool force=false )
    266         {
    267             bool update = final;
    268             if( new_gray_level < 0 )
    269                 new_gray_level = gray_level;
    270             if( !history || (history->size != size && size > 0 &&
    271                 (gray_level != history->val || force)))
    272             {
    273                 CompHistory* h = hptr++;
    274                 h->parent_ = 0;
    275                 h->child_ = history;
    276                 h->next_ = 0;
    277                 if( history )
    278                     history->parent_ = h;
    279                 h->val = gray_level;
    280                 h->size = size;
    281                 h->head = head;
    282 
    283                 history = h;
    284                 h->var = FLT_MAX;
    285                 h->checked = true;
    286                 if( h->size >= wp.p.minArea )
    287                 {
    288                     h->var = -1.f;
    289                     h->checked = false;
    290                     update = true;
    291                 }
    292             }
    293             gray_level = new_gray_level;
    294             if( update && history )
    295                 history->updateTree(wp, 0, 0, final);
    296         }
    297 
    298         // merging two connected components
    299         void merge( ConnectedComp* comp1, ConnectedComp* comp2,
    300                     CompHistory*& hptr, WParams& wp )
    301         {
    302             comp1->growHistory( hptr, wp, -1, false );
    303             comp2->growHistory( hptr, wp, -1, false );
    304 
    305             if( comp1->size < comp2->size )
    306                 std::swap(comp1, comp2);
    307 
    308             if( comp2->size == 0 )
    309             {
    310                 gray_level = comp1->gray_level;
    311                 head = comp1->head;
    312                 tail = comp1->tail;
    313                 size = comp1->size;
    314                 history = comp1->history;
    315                 return;
    316             }
    317 
    318             CompHistory* h1 = comp1->history;
    319             CompHistory* h2 = comp2->history;
    320 
    321             gray_level = std::max(comp1->gray_level, comp2->gray_level);
    322             history = comp1->history;
    323             wp.pix0[comp1->tail].setNext(comp2->head);
    324 
    325             head = comp1->head;
    326             tail = comp2->tail;
    327             size = comp1->size + comp2->size;
    328             bool keep_2nd = h2->size > wp.p.minArea;
    329             growHistory( hptr, wp, -1, false, keep_2nd );
    330             if( keep_2nd )
    331             {
    332                 h1->next_ = h2;
    333                 h2->parent_ = history;
    334             }
    335         }
    336 
    337         PPixel head;
    338         PPixel tail;
    339         CompHistory* history;
    340         int gray_level;
    341         int size;
    342     };
    343 
    344     void detectRegions( InputArray image,
    345                         std::vector<std::vector<Point> >& msers,
    346                         std::vector<Rect>& bboxes );
    347     void detect( InputArray _src, vector<KeyPoint>& keypoints, InputArray _mask );
    348 
    349     void preprocess1( const Mat& img, int* level_size )
    350     {
    351         memset(level_size, 0, 256*sizeof(level_size[0]));
    352 
    353         int i, j, cols = img.cols, rows = img.rows;
    354         int step = cols;
    355         pixbuf.resize(step*rows);
    356         heapbuf.resize(cols*rows + 256);
    357         histbuf.resize(cols*rows);
    358         Pixel borderpix;
    359         borderpix.setDir(5);
    360 
    361         for( j = 0; j < step; j++ )
    362         {
    363             pixbuf[j] = pixbuf[j + (rows-1)*step] = borderpix;
    364         }
    365 
    366         for( i = 1; i < rows-1; i++ )
    367         {
    368             const uchar* imgptr = img.ptr(i);
    369             Pixel* pptr = &pixbuf[i*step];
    370             pptr[0] = pptr[cols-1] = borderpix;
    371             for( j = 1; j < cols-1; j++ )
    372             {
    373                 int val = imgptr[j];
    374                 level_size[val]++;
    375                 pptr[j].val = 0;
    376             }
    377         }
    378     }
    379 
    380     void preprocess2( const Mat& img, int* level_size )
    381     {
    382         int i;
    383 
    384         for( i = 0; i < 128; i++ )
    385             std::swap(level_size[i], level_size[255-i]);
    386 
    387         if( !params.pass2Only )
    388         {
    389             int j, cols = img.cols, rows = img.rows;
    390             int step = cols;
    391             for( i = 1; i < rows-1; i++ )
    392             {
    393                 Pixel* pptr = &pixbuf[i*step + 1];
    394                 for( j = 1; j < cols-1; j++ )
    395                 {
    396                     pptr[j].val = 0;
    397                 }
    398             }
    399         }
    400     }
    401 
    402     void pass( const Mat& img, vector<vector<Point> >& msers, vector<Rect>& bboxvec,
    403               Size size, const int* level_size, int mask )
    404     {
    405         CompHistory* histptr = &histbuf[0];
    406         int step = size.width;
    407         Pixel *ptr0 = &pixbuf[0], *ptr = &ptr0[step+1];
    408         const uchar* imgptr0 = img.ptr();
    409         Pixel** heap[256];
    410         ConnectedComp comp[257];
    411         ConnectedComp* comptr = &comp[0];
    412         WParams wp;
    413         wp.p = params;
    414         wp.msers = &msers;
    415         wp.bboxvec = &bboxvec;
    416         wp.pix0 = ptr0;
    417         wp.step = step;
    418 
    419         heap[0] = &heapbuf[0];
    420         heap[0][0] = 0;
    421 
    422         for( int i = 1; i < 256; i++ )
    423         {
    424             heap[i] = heap[i-1] + level_size[i-1] + 1;
    425             heap[i][0] = 0;
    426         }
    427 
    428         comptr->gray_level = 256;
    429         comptr++;
    430         comptr->gray_level = ptr->getGray(ptr0, imgptr0, mask);
    431         ptr->setDir(1);
    432         int dir[] = { 0, 1, step, -1, -step };
    433         for( ;; )
    434         {
    435             int curr_gray = ptr->getGray(ptr0, imgptr0, mask);
    436             int nbr_idx = ptr->getDir();
    437             // take tour of all the 4 directions
    438             for( ; nbr_idx <= 4; nbr_idx++ )
    439             {
    440                 // get the neighbor
    441                 Pixel* ptr_nbr = ptr + dir[nbr_idx];
    442                 if( !ptr_nbr->isVisited() )
    443                 {
    444                     // set dir=1, next=0
    445                     ptr_nbr->val = 1 << DIR_SHIFT;
    446                     int nbr_gray = ptr_nbr->getGray(ptr0, imgptr0, mask);
    447                     if( nbr_gray < curr_gray )
    448                     {
    449                         // when the value of neighbor smaller than current
    450                         // push current to boundary heap and make the neighbor to be the current one
    451                         // create an empty comp
    452                         *(++heap[curr_gray]) = ptr;
    453                         ptr->val = (nbr_idx+1) << DIR_SHIFT;
    454                         ptr = ptr_nbr;
    455                         comptr++;
    456                         comptr->init(nbr_gray);
    457                         curr_gray = nbr_gray;
    458                         nbr_idx = 0;
    459                         continue;
    460                     }
    461                     // otherwise, push the neighbor to boundary heap
    462                     *(++heap[nbr_gray]) = ptr_nbr;
    463                 }
    464             }
    465 
    466             // set dir = nbr_idx, next = 0
    467             ptr->val = nbr_idx << DIR_SHIFT;
    468             int ptrofs = (int)(ptr - ptr0);
    469             CV_Assert(ptrofs != 0);
    470 
    471             // add a pixel to the pixel list
    472             if( comptr->tail )
    473                 ptr0[comptr->tail].setNext(ptrofs);
    474             else
    475                 comptr->head = ptrofs;
    476             comptr->tail = ptrofs;
    477             comptr->size++;
    478             // get the next pixel from boundary heap
    479             if( *heap[curr_gray] )
    480             {
    481                 ptr = *heap[curr_gray];
    482                 heap[curr_gray]--;
    483             }
    484             else
    485             {
    486                 for( curr_gray++; curr_gray < 256; curr_gray++ )
    487                 {
    488                     if( *heap[curr_gray] )
    489                         break;
    490                 }
    491                 if( curr_gray >= 256 )
    492                     break;
    493 
    494                 ptr = *heap[curr_gray];
    495                 heap[curr_gray]--;
    496 
    497                 if( curr_gray < comptr[-1].gray_level )
    498                     comptr->growHistory(histptr, wp, curr_gray, false);
    499                 else
    500                 {
    501                     // keep merging top two comp in stack until the gray level >= pixel_val
    502                     for(;;)
    503                     {
    504                         comptr--;
    505                         comptr->merge(comptr, comptr+1, histptr, wp);
    506                         if( curr_gray <= comptr[0].gray_level )
    507                             break;
    508                         if( curr_gray < comptr[-1].gray_level )
    509                         {
    510                             comptr->growHistory(histptr, wp, curr_gray, false);
    511                             break;
    512                         }
    513                     }
    514                 }
    515             }
    516         }
    517 
    518         for( ; comptr->gray_level != 256; comptr-- )
    519         {
    520             comptr->growHistory(histptr, wp, 256, true, true);
    521         }
    522     }
    523 
    524     Mat tempsrc;
    525     vector<Pixel> pixbuf;
    526     vector<Pixel*> heapbuf;
    527     vector<CompHistory> histbuf;
    528 
    529     Params params;
    530 };
    531 
    532 /*
    533 
    534 TODO:
    535 the color MSER has not been completely refactored yet. We leave it mostly as-is,
    536 with just enough changes to convert C structures to C++ ones and
    537 add support for color images into MSER_Impl::detectAndLabel.
    538 */
    539 
    540 const int TABLE_SIZE = 400;
    541 
    542 static const float chitab3[]=
    543 {
    544     0.f,  0.0150057f,  0.0239478f,  0.0315227f,
    545     0.0383427f,  0.0446605f,  0.0506115f,  0.0562786f,
    546     0.0617174f,  0.0669672f,  0.0720573f,  0.0770099f,
    547     0.081843f,  0.0865705f,  0.0912043f,  0.0957541f,
    548     0.100228f,  0.104633f,  0.108976f,  0.113261f,
    549     0.117493f,  0.121676f,  0.125814f,  0.12991f,
    550     0.133967f,  0.137987f,  0.141974f,  0.145929f,
    551     0.149853f,  0.15375f,  0.15762f,  0.161466f,
    552     0.165287f,  0.169087f,  0.172866f,  0.176625f,
    553     0.180365f,  0.184088f,  0.187794f,  0.191483f,
    554     0.195158f,  0.198819f,  0.202466f,  0.2061f,
    555     0.209722f,  0.213332f,  0.216932f,  0.220521f,
    556     0.2241f,  0.22767f,  0.231231f,  0.234783f,
    557     0.238328f,  0.241865f,  0.245395f,  0.248918f,
    558     0.252435f,  0.255947f,  0.259452f,  0.262952f,
    559     0.266448f,  0.269939f,  0.273425f,  0.276908f,
    560     0.280386f,  0.283862f,  0.287334f,  0.290803f,
    561     0.29427f,  0.297734f,  0.301197f,  0.304657f,
    562     0.308115f,  0.311573f,  0.315028f,  0.318483f,
    563     0.321937f,  0.32539f,  0.328843f,  0.332296f,
    564     0.335749f,  0.339201f,  0.342654f,  0.346108f,
    565     0.349562f,  0.353017f,  0.356473f,  0.35993f,
    566     0.363389f,  0.366849f,  0.37031f,  0.373774f,
    567     0.377239f,  0.380706f,  0.384176f,  0.387648f,
    568     0.391123f,  0.3946f,  0.39808f,  0.401563f,
    569     0.405049f,  0.408539f,  0.412032f,  0.415528f,
    570     0.419028f,  0.422531f,  0.426039f,  0.429551f,
    571     0.433066f,  0.436586f,  0.440111f,  0.44364f,
    572     0.447173f,  0.450712f,  0.454255f,  0.457803f,
    573     0.461356f,  0.464915f,  0.468479f,  0.472049f,
    574     0.475624f,  0.479205f,  0.482792f,  0.486384f,
    575     0.489983f,  0.493588f,  0.4972f,  0.500818f,
    576     0.504442f,  0.508073f,  0.511711f,  0.515356f,
    577     0.519008f,  0.522667f,  0.526334f,  0.530008f,
    578     0.533689f,  0.537378f,  0.541075f,  0.54478f,
    579     0.548492f,  0.552213f,  0.555942f,  0.55968f,
    580     0.563425f,  0.56718f,  0.570943f,  0.574715f,
    581     0.578497f,  0.582287f,  0.586086f,  0.589895f,
    582     0.593713f,  0.597541f,  0.601379f,  0.605227f,
    583     0.609084f,  0.612952f,  0.61683f,  0.620718f,
    584     0.624617f,  0.628526f,  0.632447f,  0.636378f,
    585     0.64032f,  0.644274f,  0.648239f,  0.652215f,
    586     0.656203f,  0.660203f,  0.664215f,  0.668238f,
    587     0.672274f,  0.676323f,  0.680384f,  0.684457f,
    588     0.688543f,  0.692643f,  0.696755f,  0.700881f,
    589     0.70502f,  0.709172f,  0.713339f,  0.717519f,
    590     0.721714f,  0.725922f,  0.730145f,  0.734383f,
    591     0.738636f,  0.742903f,  0.747185f,  0.751483f,
    592     0.755796f,  0.760125f,  0.76447f,  0.768831f,
    593     0.773208f,  0.777601f,  0.782011f,  0.786438f,
    594     0.790882f,  0.795343f,  0.799821f,  0.804318f,
    595     0.808831f,  0.813363f,  0.817913f,  0.822482f,
    596     0.827069f,  0.831676f,  0.836301f,  0.840946f,
    597     0.84561f,  0.850295f,  0.854999f,  0.859724f,
    598     0.864469f,  0.869235f,  0.874022f,  0.878831f,
    599     0.883661f,  0.888513f,  0.893387f,  0.898284f,
    600     0.903204f,  0.908146f,  0.913112f,  0.918101f,
    601     0.923114f,  0.928152f,  0.933214f,  0.938301f,
    602     0.943413f,  0.94855f,  0.953713f,  0.958903f,
    603     0.964119f,  0.969361f,  0.974631f,  0.979929f,
    604     0.985254f,  0.990608f,  0.99599f,  1.0014f,
    605     1.00684f,  1.01231f,  1.01781f,  1.02335f,
    606     1.02891f,  1.0345f,  1.04013f,  1.04579f,
    607     1.05148f,  1.05721f,  1.06296f,  1.06876f,
    608     1.07459f,  1.08045f,  1.08635f,  1.09228f,
    609     1.09826f,  1.10427f,  1.11032f,  1.1164f,
    610     1.12253f,  1.1287f,  1.1349f,  1.14115f,
    611     1.14744f,  1.15377f,  1.16015f,  1.16656f,
    612     1.17303f,  1.17954f,  1.18609f,  1.19269f,
    613     1.19934f,  1.20603f,  1.21278f,  1.21958f,
    614     1.22642f,  1.23332f,  1.24027f,  1.24727f,
    615     1.25433f,  1.26144f,  1.26861f,  1.27584f,
    616     1.28312f,  1.29047f,  1.29787f,  1.30534f,
    617     1.31287f,  1.32046f,  1.32812f,  1.33585f,
    618     1.34364f,  1.3515f,  1.35943f,  1.36744f,
    619     1.37551f,  1.38367f,  1.39189f,  1.4002f,
    620     1.40859f,  1.41705f,  1.42561f,  1.43424f,
    621     1.44296f,  1.45177f,  1.46068f,  1.46967f,
    622     1.47876f,  1.48795f,  1.49723f,  1.50662f,
    623     1.51611f,  1.52571f,  1.53541f,  1.54523f,
    624     1.55517f,  1.56522f,  1.57539f,  1.58568f,
    625     1.59611f,  1.60666f,  1.61735f,  1.62817f,
    626     1.63914f,  1.65025f,  1.66152f,  1.67293f,
    627     1.68451f,  1.69625f,  1.70815f,  1.72023f,
    628     1.73249f,  1.74494f,  1.75757f,  1.77041f,
    629     1.78344f,  1.79669f,  1.81016f,  1.82385f,
    630     1.83777f,  1.85194f,  1.86635f,  1.88103f,
    631     1.89598f,  1.91121f,  1.92674f,  1.94257f,
    632     1.95871f,  1.97519f,  1.99201f,  2.0092f,
    633     2.02676f,  2.04471f,  2.06309f,  2.08189f,
    634     2.10115f,  2.12089f,  2.14114f,  2.16192f,
    635     2.18326f,  2.2052f,  2.22777f,  2.25101f,
    636     2.27496f,  2.29966f,  2.32518f,  2.35156f,
    637     2.37886f,  2.40717f,  2.43655f,  2.46709f,
    638     2.49889f,  2.53206f,  2.56673f,  2.60305f,
    639     2.64117f,  2.6813f,  2.72367f,  2.76854f,
    640     2.81623f,  2.86714f,  2.92173f,  2.98059f,
    641     3.04446f,  3.1143f,  3.19135f,  3.27731f,
    642     3.37455f,  3.48653f,  3.61862f,  3.77982f,
    643     3.98692f,  4.2776f,  4.77167f,  133.333f
    644 };
    645 
    646 struct MSCRNode;
    647 
    648 struct TempMSCR
    649 {
    650     MSCRNode* head;
    651     MSCRNode* tail;
    652     double m; // the margin used to prune area later
    653     int size;
    654 };
    655 
    656 struct MSCRNode
    657 {
    658     MSCRNode* shortcut;
    659     // to make the finding of root less painful
    660     MSCRNode* prev;
    661     MSCRNode* next;
    662     // a point double-linked list
    663     TempMSCR* tmsr;
    664     // the temporary msr (set to NULL at every re-initialise)
    665     TempMSCR* gmsr;
    666     // the global msr (once set, never to NULL)
    667     int index;
    668     // the index of the node, at this point, it should be x at the first 16-bits, and y at the last 16-bits.
    669     int rank;
    670     int reinit;
    671     int size, sizei;
    672     double dt, di;
    673     double s;
    674 };
    675 
    676 struct MSCREdge
    677 {
    678     double chi;
    679     MSCRNode* left;
    680     MSCRNode* right;
    681 };
    682 
    683 static double ChiSquaredDistance( const uchar* x, const uchar* y )
    684 {
    685     return (double)((x[0]-y[0])*(x[0]-y[0]))/(double)(x[0]+y[0]+1e-10)+
    686     (double)((x[1]-y[1])*(x[1]-y[1]))/(double)(x[1]+y[1]+1e-10)+
    687     (double)((x[2]-y[2])*(x[2]-y[2]))/(double)(x[2]+y[2]+1e-10);
    688 }
    689 
    690 static void initMSCRNode( MSCRNode* node )
    691 {
    692     node->gmsr = node->tmsr = NULL;
    693     node->reinit = 0xffff;
    694     node->rank = 0;
    695     node->sizei = node->size = 1;
    696     node->prev = node->next = node->shortcut = node;
    697 }
    698 
    699 // the preprocess to get the edge list with proper gaussian blur
    700 static int preprocessMSER_8uC3( MSCRNode* node,
    701                                MSCREdge* edge,
    702                                double* total,
    703                                const Mat& src,
    704                                Mat& dx,
    705                                Mat& dy,
    706                                int Ne,
    707                                int edgeBlurSize )
    708 {
    709     int srccpt = (int)(src.step-src.cols*3);
    710     const uchar* srcptr = src.ptr();
    711     const uchar* lastptr = srcptr+3;
    712     double* dxptr = dx.ptr<double>();
    713     for ( int i = 0; i < src.rows; i++ )
    714     {
    715         for ( int j = 0; j < src.cols-1; j++ )
    716         {
    717             *dxptr = ChiSquaredDistance( srcptr, lastptr );
    718             dxptr++;
    719             srcptr += 3;
    720             lastptr += 3;
    721         }
    722         srcptr += srccpt+3;
    723         lastptr += srccpt+3;
    724     }
    725     srcptr = src.ptr();
    726     lastptr = srcptr+src.step;
    727     double* dyptr = dy.ptr<double>();
    728     for ( int i = 0; i < src.rows-1; i++ )
    729     {
    730         for ( int j = 0; j < src.cols; j++ )
    731         {
    732             *dyptr = ChiSquaredDistance( srcptr, lastptr );
    733             dyptr++;
    734             srcptr += 3;
    735             lastptr += 3;
    736         }
    737         srcptr += srccpt;
    738         lastptr += srccpt;
    739     }
    740     // get dx and dy and blur it
    741     if ( edgeBlurSize >= 1 )
    742     {
    743         GaussianBlur( dx, dx, Size(edgeBlurSize, edgeBlurSize), 0 );
    744         GaussianBlur( dy, dy, Size(edgeBlurSize, edgeBlurSize), 0 );
    745     }
    746     dxptr = dx.ptr<double>();
    747     dyptr = dy.ptr<double>();
    748     // assian dx, dy to proper edge list and initialize mscr node
    749     // the nasty code here intended to avoid extra loops
    750     MSCRNode* nodeptr = node;
    751     initMSCRNode( nodeptr );
    752     nodeptr->index = 0;
    753     *total += edge->chi = *dxptr;
    754     dxptr++;
    755     edge->left = nodeptr;
    756     edge->right = nodeptr+1;
    757     edge++;
    758     nodeptr++;
    759     for ( int i = 1; i < src.cols-1; i++ )
    760     {
    761         initMSCRNode( nodeptr );
    762         nodeptr->index = i;
    763         *total += edge->chi = *dxptr;
    764         dxptr++;
    765         edge->left = nodeptr;
    766         edge->right = nodeptr+1;
    767         edge++;
    768         nodeptr++;
    769     }
    770     initMSCRNode( nodeptr );
    771     nodeptr->index = src.cols-1;
    772     nodeptr++;
    773     for ( int i = 1; i < src.rows-1; i++ )
    774     {
    775         initMSCRNode( nodeptr );
    776         nodeptr->index = i<<16;
    777         *total += edge->chi = *dyptr;
    778         dyptr++;
    779         edge->left = nodeptr-src.cols;
    780         edge->right = nodeptr;
    781         edge++;
    782         *total += edge->chi = *dxptr;
    783         dxptr++;
    784         edge->left = nodeptr;
    785         edge->right = nodeptr+1;
    786         edge++;
    787         nodeptr++;
    788         for ( int j = 1; j < src.cols-1; j++ )
    789         {
    790             initMSCRNode( nodeptr );
    791             nodeptr->index = (i<<16)|j;
    792             *total += edge->chi = *dyptr;
    793             dyptr++;
    794             edge->left = nodeptr-src.cols;
    795             edge->right = nodeptr;
    796             edge++;
    797             *total += edge->chi = *dxptr;
    798             dxptr++;
    799             edge->left = nodeptr;
    800             edge->right = nodeptr+1;
    801             edge++;
    802             nodeptr++;
    803         }
    804         initMSCRNode( nodeptr );
    805         nodeptr->index = (i<<16)|(src.cols-1);
    806         *total += edge->chi = *dyptr;
    807         dyptr++;
    808         edge->left = nodeptr-src.cols;
    809         edge->right = nodeptr;
    810         edge++;
    811         nodeptr++;
    812     }
    813     initMSCRNode( nodeptr );
    814     nodeptr->index = (src.rows-1)<<16;
    815     *total += edge->chi = *dxptr;
    816     dxptr++;
    817     edge->left = nodeptr;
    818     edge->right = nodeptr+1;
    819     edge++;
    820     *total += edge->chi = *dyptr;
    821     dyptr++;
    822     edge->left = nodeptr-src.cols;
    823     edge->right = nodeptr;
    824     edge++;
    825     nodeptr++;
    826     for ( int i = 1; i < src.cols-1; i++ )
    827     {
    828         initMSCRNode( nodeptr );
    829         nodeptr->index = ((src.rows-1)<<16)|i;
    830         *total += edge->chi = *dxptr;
    831         dxptr++;
    832         edge->left = nodeptr;
    833         edge->right = nodeptr+1;
    834         edge++;
    835         *total += edge->chi = *dyptr;
    836         dyptr++;
    837         edge->left = nodeptr-src.cols;
    838         edge->right = nodeptr;
    839         edge++;
    840         nodeptr++;
    841     }
    842     initMSCRNode( nodeptr );
    843     nodeptr->index = ((src.rows-1)<<16)|(src.cols-1);
    844     *total += edge->chi = *dyptr;
    845     edge->left = nodeptr-src.cols;
    846     edge->right = nodeptr;
    847 
    848     return Ne;
    849 }
    850 
    851 class LessThanEdge
    852 {
    853 public:
    854     bool operator()(const MSCREdge& a, const MSCREdge& b) const { return a.chi < b.chi; }
    855 };
    856 
    857 // to find the root of one region
    858 static MSCRNode* findMSCR( MSCRNode* x )
    859 {
    860     MSCRNode* prev = x;
    861     MSCRNode* next;
    862     for ( ; ; )
    863     {
    864         next = x->shortcut;
    865         x->shortcut = prev;
    866         if ( next == x ) break;
    867         prev= x;
    868         x = next;
    869     }
    870     MSCRNode* root = x;
    871     for ( ; ; )
    872     {
    873         prev = x->shortcut;
    874         x->shortcut = root;
    875         if ( prev == x ) break;
    876         x = prev;
    877     }
    878     return root;
    879 }
    880 
    881 // the stable mscr should be:
    882 // bigger than minArea and smaller than maxArea
    883 // differ from its ancestor more than minDiversity
    884 static bool MSCRStableCheck( MSCRNode* x, const MSER_Impl::Params& params )
    885 {
    886     if ( x->size <= params.minArea || x->size >= params.maxArea )
    887         return false;
    888     if ( x->gmsr == NULL )
    889         return true;
    890     double div = (double)(x->size-x->gmsr->size)/(double)x->size;
    891     return div > params.minDiversity;
    892 }
    893 
    894 static void
    895 extractMSER_8uC3( const Mat& src,
    896                   vector<vector<Point> >& msers,
    897                   vector<Rect>& bboxvec,
    898                   const MSER_Impl::Params& params )
    899 {
    900     bboxvec.clear();
    901     MSCRNode* map = (MSCRNode*)cvAlloc( src.cols*src.rows*sizeof(map[0]) );
    902     int Ne = src.cols*src.rows*2-src.cols-src.rows;
    903     MSCREdge* edge = (MSCREdge*)cvAlloc( Ne*sizeof(edge[0]) );
    904     TempMSCR* mscr = (TempMSCR*)cvAlloc( src.cols*src.rows*sizeof(mscr[0]) );
    905     double emean = 0;
    906     Mat dx( src.rows, src.cols-1, CV_64FC1 );
    907     Mat dy( src.rows-1, src.cols, CV_64FC1 );
    908     Ne = preprocessMSER_8uC3( map, edge, &emean, src, dx, dy, Ne, params.edgeBlurSize );
    909     emean = emean / (double)Ne;
    910     std::sort(edge, edge + Ne, LessThanEdge());
    911     MSCREdge* edge_ub = edge+Ne;
    912     MSCREdge* edgeptr = edge;
    913     TempMSCR* mscrptr = mscr;
    914     // the evolution process
    915     for ( int i = 0; i < params.maxEvolution; i++ )
    916     {
    917         double k = (double)i/(double)params.maxEvolution*(TABLE_SIZE-1);
    918         int ti = cvFloor(k);
    919         double reminder = k-ti;
    920         double thres = emean*(chitab3[ti]*(1-reminder)+chitab3[ti+1]*reminder);
    921         // to process all the edges in the list that chi < thres
    922         while ( edgeptr < edge_ub && edgeptr->chi < thres )
    923         {
    924             MSCRNode* lr = findMSCR( edgeptr->left );
    925             MSCRNode* rr = findMSCR( edgeptr->right );
    926             // get the region root (who is responsible)
    927             if ( lr != rr )
    928             {
    929                 // rank idea take from: N-tree Disjoint-Set Forests for Maximally Stable Extremal Regions
    930                 if ( rr->rank > lr->rank )
    931                 {
    932                     MSCRNode* tmp;
    933                     CV_SWAP( lr, rr, tmp );
    934                 } else if ( lr->rank == rr->rank ) {
    935                     // at the same rank, we will compare the size
    936                     if ( lr->size > rr->size )
    937                     {
    938                         MSCRNode* tmp;
    939                         CV_SWAP( lr, rr, tmp );
    940                     }
    941                     lr->rank++;
    942                 }
    943                 rr->shortcut = lr;
    944                 lr->size += rr->size;
    945                 // join rr to the end of list lr (lr is a endless double-linked list)
    946                 lr->prev->next = rr;
    947                 lr->prev = rr->prev;
    948                 rr->prev->next = lr;
    949                 rr->prev = lr;
    950                 // area threshold force to reinitialize
    951                 if ( lr->size > (lr->size-rr->size)*params.areaThreshold )
    952                 {
    953                     lr->sizei = lr->size;
    954                     lr->reinit = i;
    955                     if ( lr->tmsr != NULL )
    956                     {
    957                         lr->tmsr->m = lr->dt-lr->di;
    958                         lr->tmsr = NULL;
    959                     }
    960                     lr->di = edgeptr->chi;
    961                     lr->s = 1e10;
    962                 }
    963                 lr->dt = edgeptr->chi;
    964                 if ( i > lr->reinit )
    965                 {
    966                     double s = (double)(lr->size-lr->sizei)/(lr->dt-lr->di);
    967                     if ( s < lr->s )
    968                     {
    969                         // skip the first one and check stablity
    970                         if ( i > lr->reinit+1 && MSCRStableCheck( lr, params ) )
    971                         {
    972                             if ( lr->tmsr == NULL )
    973                             {
    974                                 lr->gmsr = lr->tmsr = mscrptr;
    975                                 mscrptr++;
    976                             }
    977                             lr->tmsr->size = lr->size;
    978                             lr->tmsr->head = lr;
    979                             lr->tmsr->tail = lr->prev;
    980                             lr->tmsr->m = 0;
    981                         }
    982                         lr->s = s;
    983                     }
    984                 }
    985             }
    986             edgeptr++;
    987         }
    988         if ( edgeptr >= edge_ub )
    989             break;
    990     }
    991     for ( TempMSCR* ptr = mscr; ptr < mscrptr; ptr++ )
    992         // to prune area with margin less than minMargin
    993         if ( ptr->m > params.minMargin )
    994         {
    995             MSCRNode* lpt = ptr->head;
    996             int xmin = INT_MAX, ymin = INT_MAX, xmax = INT_MIN, ymax = INT_MIN;
    997             msers.push_back(vector<Point>());
    998             vector<Point>& mser = msers.back();
    999 
   1000             for ( int i = 0; i < ptr->size; i++ )
   1001             {
   1002                 Point pt;
   1003                 pt.x = (lpt->index)&0xffff;
   1004                 pt.y = (lpt->index)>>16;
   1005                 xmin = std::min(xmin, pt.x);
   1006                 xmax = std::max(xmax, pt.x);
   1007                 ymin = std::min(ymin, pt.y);
   1008                 ymax = std::max(ymax, pt.y);
   1009 
   1010                 lpt = lpt->next;
   1011                 mser.push_back(pt);
   1012             }
   1013             bboxvec.push_back(Rect(xmin, ymin, xmax - xmin + 1, ymax - ymin + 1));
   1014         }
   1015     cvFree( &mscr );
   1016     cvFree( &edge );
   1017     cvFree( &map );
   1018 }
   1019 
   1020 void MSER_Impl::detectRegions( InputArray _src, vector<vector<Point> >& msers, vector<Rect>& bboxes )
   1021 {
   1022     Mat src = _src.getMat();
   1023     size_t npix = src.total();
   1024 
   1025     msers.clear();
   1026     bboxes.clear();
   1027 
   1028     if( npix == 0 )
   1029         return;
   1030 
   1031     Size size = src.size();
   1032 
   1033     if( src.type() == CV_8U )
   1034     {
   1035         int level_size[256];
   1036         if( !src.isContinuous() )
   1037         {
   1038             src.copyTo(tempsrc);
   1039             src = tempsrc;
   1040         }
   1041 
   1042         // darker to brighter (MSER+)
   1043         preprocess1( src, level_size );
   1044         if( !params.pass2Only )
   1045             pass( src, msers, bboxes, size, level_size, 0 );
   1046         // brighter to darker (MSER-)
   1047         preprocess2( src, level_size );
   1048         pass( src, msers, bboxes, size, level_size, 255 );
   1049     }
   1050     else
   1051     {
   1052         CV_Assert( src.type() == CV_8UC3 || src.type() == CV_8UC4 );
   1053         extractMSER_8uC3( src, msers, bboxes, params );
   1054     }
   1055 }
   1056 
   1057 void MSER_Impl::detect( InputArray _image, vector<KeyPoint>& keypoints, InputArray _mask )
   1058 {
   1059     vector<Rect> bboxes;
   1060     vector<vector<Point> > msers;
   1061     Mat mask = _mask.getMat();
   1062 
   1063     detectRegions(_image, msers, bboxes);
   1064     int i, ncomps = (int)msers.size();
   1065 
   1066     keypoints.clear();
   1067     for( i = 0; i < ncomps; i++ )
   1068     {
   1069         Rect r = bboxes[i];
   1070         // TODO check transformation from MSER region to KeyPoint
   1071         RotatedRect rect = fitEllipse(Mat(msers[i]));
   1072         float diam = std::sqrt(rect.size.height*rect.size.width);
   1073 
   1074         if( diam > std::numeric_limits<float>::epsilon() && r.contains(rect.center) &&
   1075             (mask.empty() || mask.at<uchar>(cvRound(rect.center.y), cvRound(rect.center.x)) != 0) )
   1076             keypoints.push_back( KeyPoint(rect.center, diam) );
   1077     }
   1078 }
   1079 
   1080 Ptr<MSER> MSER::create( int _delta, int _min_area, int _max_area,
   1081       double _max_variation, double _min_diversity,
   1082       int _max_evolution, double _area_threshold,
   1083       double _min_margin, int _edge_blur_size )
   1084 {
   1085     return makePtr<MSER_Impl>(
   1086         MSER_Impl::Params(_delta, _min_area, _max_area,
   1087                           _max_variation, _min_diversity,
   1088                           _max_evolution, _area_threshold,
   1089                           _min_margin, _edge_blur_size));
   1090 }
   1091 
   1092 }
   1093