1 /* Redistribution and use in source and binary forms, with or
2 * without modification, are permitted provided that the following
4 * Redistributions of source code must retain the above
5 * copyright notice, this list of conditions and the following
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.
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
28 * Copyright© 2009, Liu Liu All rights reserved.
30 * OpenCV functions for MSER extraction
32 * 1. there are two different implementation of MSER, one for grey image, one for color image
33 * 2. the grey 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 grey 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.
42 #include "precomp.hpp"
47 const int TABLE_SIZE = 400;
49 static double chitab3[]={0, 0.0150057, 0.0239478, 0.0315227,
50 0.0383427, 0.0446605, 0.0506115, 0.0562786,
51 0.0617174, 0.0669672, 0.0720573, 0.0770099,
52 0.081843, 0.0865705, 0.0912043, 0.0957541,
53 0.100228, 0.104633, 0.108976, 0.113261,
54 0.117493, 0.121676, 0.125814, 0.12991,
55 0.133967, 0.137987, 0.141974, 0.145929,
56 0.149853, 0.15375, 0.15762, 0.161466,
57 0.165287, 0.169087, 0.172866, 0.176625,
58 0.180365, 0.184088, 0.187794, 0.191483,
59 0.195158, 0.198819, 0.202466, 0.2061,
60 0.209722, 0.213332, 0.216932, 0.220521,
61 0.2241, 0.22767, 0.231231, 0.234783,
62 0.238328, 0.241865, 0.245395, 0.248918,
63 0.252435, 0.255947, 0.259452, 0.262952,
64 0.266448, 0.269939, 0.273425, 0.276908,
65 0.280386, 0.283862, 0.287334, 0.290803,
66 0.29427, 0.297734, 0.301197, 0.304657,
67 0.308115, 0.311573, 0.315028, 0.318483,
68 0.321937, 0.32539, 0.328843, 0.332296,
69 0.335749, 0.339201, 0.342654, 0.346108,
70 0.349562, 0.353017, 0.356473, 0.35993,
71 0.363389, 0.366849, 0.37031, 0.373774,
72 0.377239, 0.380706, 0.384176, 0.387648,
73 0.391123, 0.3946, 0.39808, 0.401563,
74 0.405049, 0.408539, 0.412032, 0.415528,
75 0.419028, 0.422531, 0.426039, 0.429551,
76 0.433066, 0.436586, 0.440111, 0.44364,
77 0.447173, 0.450712, 0.454255, 0.457803,
78 0.461356, 0.464915, 0.468479, 0.472049,
79 0.475624, 0.479205, 0.482792, 0.486384,
80 0.489983, 0.493588, 0.4972, 0.500818,
81 0.504442, 0.508073, 0.511711, 0.515356,
82 0.519008, 0.522667, 0.526334, 0.530008,
83 0.533689, 0.537378, 0.541075, 0.54478,
84 0.548492, 0.552213, 0.555942, 0.55968,
85 0.563425, 0.56718, 0.570943, 0.574715,
86 0.578497, 0.582287, 0.586086, 0.589895,
87 0.593713, 0.597541, 0.601379, 0.605227,
88 0.609084, 0.612952, 0.61683, 0.620718,
89 0.624617, 0.628526, 0.632447, 0.636378,
90 0.64032, 0.644274, 0.648239, 0.652215,
91 0.656203, 0.660203, 0.664215, 0.668238,
92 0.672274, 0.676323, 0.680384, 0.684457,
93 0.688543, 0.692643, 0.696755, 0.700881,
94 0.70502, 0.709172, 0.713339, 0.717519,
95 0.721714, 0.725922, 0.730145, 0.734383,
96 0.738636, 0.742903, 0.747185, 0.751483,
97 0.755796, 0.760125, 0.76447, 0.768831,
98 0.773208, 0.777601, 0.782011, 0.786438,
99 0.790882, 0.795343, 0.799821, 0.804318,
100 0.808831, 0.813363, 0.817913, 0.822482,
101 0.827069, 0.831676, 0.836301, 0.840946,
102 0.84561, 0.850295, 0.854999, 0.859724,
103 0.864469, 0.869235, 0.874022, 0.878831,
104 0.883661, 0.888513, 0.893387, 0.898284,
105 0.903204, 0.908146, 0.913112, 0.918101,
106 0.923114, 0.928152, 0.933214, 0.938301,
107 0.943413, 0.94855, 0.953713, 0.958903,
108 0.964119, 0.969361, 0.974631, 0.979929,
109 0.985254, 0.990608, 0.99599, 1.0014,
110 1.00684, 1.01231, 1.01781, 1.02335,
111 1.02891, 1.0345, 1.04013, 1.04579,
112 1.05148, 1.05721, 1.06296, 1.06876,
113 1.07459, 1.08045, 1.08635, 1.09228,
114 1.09826, 1.10427, 1.11032, 1.1164,
115 1.12253, 1.1287, 1.1349, 1.14115,
116 1.14744, 1.15377, 1.16015, 1.16656,
117 1.17303, 1.17954, 1.18609, 1.19269,
118 1.19934, 1.20603, 1.21278, 1.21958,
119 1.22642, 1.23332, 1.24027, 1.24727,
120 1.25433, 1.26144, 1.26861, 1.27584,
121 1.28312, 1.29047, 1.29787, 1.30534,
122 1.31287, 1.32046, 1.32812, 1.33585,
123 1.34364, 1.3515, 1.35943, 1.36744,
124 1.37551, 1.38367, 1.39189, 1.4002,
125 1.40859, 1.41705, 1.42561, 1.43424,
126 1.44296, 1.45177, 1.46068, 1.46967,
127 1.47876, 1.48795, 1.49723, 1.50662,
128 1.51611, 1.52571, 1.53541, 1.54523,
129 1.55517, 1.56522, 1.57539, 1.58568,
130 1.59611, 1.60666, 1.61735, 1.62817,
131 1.63914, 1.65025, 1.66152, 1.67293,
132 1.68451, 1.69625, 1.70815, 1.72023,
133 1.73249, 1.74494, 1.75757, 1.77041,
134 1.78344, 1.79669, 1.81016, 1.82385,
135 1.83777, 1.85194, 1.86635, 1.88103,
136 1.89598, 1.91121, 1.92674, 1.94257,
137 1.95871, 1.97519, 1.99201, 2.0092,
138 2.02676, 2.04471, 2.06309, 2.08189,
139 2.10115, 2.12089, 2.14114, 2.16192,
140 2.18326, 2.2052, 2.22777, 2.25101,
141 2.27496, 2.29966, 2.32518, 2.35156,
142 2.37886, 2.40717, 2.43655, 2.46709,
143 2.49889, 2.53206, 2.56673, 2.60305,
144 2.64117, 2.6813, 2.72367, 2.76854,
145 2.81623, 2.86714, 2.92173, 2.98059,
146 3.04446, 3.1143, 3.19135, 3.27731,
147 3.37455, 3.48653, 3.61862, 3.77982,
148 3.98692, 4.2776, 4.77167, 133.333 };
150 typedef struct LinkedPoint
152 struct LinkedPoint* prev;
153 struct LinkedPoint* next;
158 // the history of region grown
159 typedef struct MSERGrowHistory
161 struct MSERGrowHistory* shortcut;
162 struct MSERGrowHistory* child;
163 int stable; // when it ever stabled before, record the size
169 typedef struct MSERConnectedComp
173 MSERGrowHistory* history;
174 unsigned long grey_level;
176 int dvar; // the derivative of last var
177 float var; // the current variation (most time is the variation of one-step back)
181 // Linear Time MSER claims by using bsf can get performance gain, here is the implementation
182 // however it seems that will not do any good in real world test
183 inline void _bitset(unsigned long * a, unsigned long b)
187 inline void _bitreset(unsigned long * a, unsigned long b)
194 MSERParams( int _delta, int _minArea, int _maxArea, double _maxVariation,
195 double _minDiversity, int _maxEvolution, double _areaThreshold,
196 double _minMargin, int _edgeBlurSize )
197 : delta(_delta), minArea(_minArea), maxArea(_maxArea), maxVariation(_maxVariation),
198 minDiversity(_minDiversity), maxEvolution(_maxEvolution), areaThreshold(_areaThreshold),
199 minMargin(_minMargin), edgeBlurSize(_edgeBlurSize)
207 double areaThreshold;
212 // clear the connected component in stack
214 initMSERComp( MSERConnectedComp* comp )
219 comp->history = NULL;
222 // add history of size to a connected component
224 MSERNewHistory( MSERConnectedComp* comp, MSERGrowHistory* history )
226 history->child = history;
227 if ( NULL == comp->history )
229 history->shortcut = history;
232 comp->history->child = history;
233 history->shortcut = comp->history->shortcut;
234 history->stable = comp->history->stable;
236 history->val = comp->grey_level;
237 history->size = comp->size;
238 comp->history = history;
241 // merging two connected component
243 MSERMergeComp( MSERConnectedComp* comp1,
244 MSERConnectedComp* comp2,
245 MSERConnectedComp* comp,
246 MSERGrowHistory* history )
250 comp->grey_level = comp2->grey_level;
251 history->child = history;
252 // select the winner by size
253 if ( comp1->size >= comp2->size )
255 if ( NULL == comp1->history )
257 history->shortcut = history;
260 comp1->history->child = history;
261 history->shortcut = comp1->history->shortcut;
262 history->stable = comp1->history->stable;
264 if ( NULL != comp2->history && comp2->history->stable > history->stable )
265 history->stable = comp2->history->stable;
266 history->val = comp1->grey_level;
267 history->size = comp1->size;
268 // put comp1 to history
269 comp->var = comp1->var;
270 comp->dvar = comp1->dvar;
271 if ( comp1->size > 0 && comp2->size > 0 )
273 comp1->tail->next = comp2->head;
274 comp2->head->prev = comp1->tail;
276 head = ( comp1->size > 0 ) ? comp1->head : comp2->head;
277 tail = ( comp2->size > 0 ) ? comp2->tail : comp1->tail;
278 // always made the newly added in the last of the pixel list (comp1 ... comp2)
280 if ( NULL == comp2->history )
282 history->shortcut = history;
285 comp2->history->child = history;
286 history->shortcut = comp2->history->shortcut;
287 history->stable = comp2->history->stable;
289 if ( NULL != comp1->history && comp1->history->stable > history->stable )
290 history->stable = comp1->history->stable;
291 history->val = comp2->grey_level;
292 history->size = comp2->size;
293 // put comp2 to history
294 comp->var = comp2->var;
295 comp->dvar = comp2->dvar;
296 if ( comp1->size > 0 && comp2->size > 0 )
298 comp2->tail->next = comp1->head;
299 comp1->head->prev = comp2->tail;
301 head = ( comp2->size > 0 ) ? comp2->head : comp1->head;
302 tail = ( comp1->size > 0 ) ? comp1->tail : comp2->tail;
303 // always made the newly added in the last of the pixel list (comp2 ... comp1)
307 comp->history = history;
308 comp->size = comp1->size + comp2->size;
312 MSERVariationCalc( MSERConnectedComp* comp, int delta )
314 MSERGrowHistory* history = comp->history;
315 int val = comp->grey_level;
316 if ( NULL != history )
318 MSERGrowHistory* shortcut = history->shortcut;
319 while ( shortcut != shortcut->shortcut && shortcut->val + delta > val )
320 shortcut = shortcut->shortcut;
321 MSERGrowHistory* child = shortcut->child;
322 while ( child != child->child && child->val + delta <= val )
325 child = child->child;
327 // get the position of history where the shortcut->val <= delta+val and shortcut->child->val >= delta+val
328 history->shortcut = shortcut;
329 return (float)(comp->size-shortcut->size)/(float)shortcut->size;
330 // here is a small modification of MSER where cal ||R_{i}-R_{i-delta}||/||R_{i-delta}||
331 // in standard MSER, cal ||R_{i+delta}-R_{i-delta}||/||R_{i}||
332 // my calculation is simpler and much easier to implement
337 static bool MSERStableCheck( MSERConnectedComp* comp, MSERParams params )
339 // tricky part: it actually check the stablity of one-step back
340 if ( comp->history == NULL || comp->history->size <= params.minArea || comp->history->size >= params.maxArea )
342 float div = (float)(comp->history->size-comp->history->stable)/(float)comp->history->size;
343 float var = MSERVariationCalc( comp, params.delta );
344 int dvar = ( comp->var < var || (unsigned long)(comp->history->val + 1) < comp->grey_level );
345 int stable = ( dvar && !comp->dvar && comp->var < params.maxVariation && div > params.minDiversity );
349 comp->history->stable = comp->history->size;
353 // add a pixel to the pixel list
354 static void accumulateMSERComp( MSERConnectedComp* comp, LinkedPoint* point )
356 if ( comp->size > 0 )
358 point->prev = comp->tail;
359 comp->tail->next = point;
370 // convert the point set to CvSeq
371 static CvContour* MSERToContour( MSERConnectedComp* comp, CvMemStorage* storage )
373 CvSeq* _contour = cvCreateSeq( CV_SEQ_KIND_GENERIC|CV_32SC2, sizeof(CvContour), sizeof(CvPoint), storage );
374 CvContour* contour = (CvContour*)_contour;
375 cvSeqPushMulti( _contour, 0, comp->history->size );
376 LinkedPoint* lpt = comp->head;
377 for ( int i = 0; i < comp->history->size; i++ )
379 CvPoint* pt = CV_GET_SEQ_ELEM( CvPoint, _contour, i );
384 cvBoundingRect( contour );
388 // to preprocess src image to following format
390 // > 0 is available, < 0 is visited
391 // 17~19 bits is the direction
392 // 8~11 bits is the bucket it falls to (for BitScanForward)
393 // 0~8 bits is the color
394 static int* preprocessMSER_8UC1( CvMat* img,
399 int srccpt = src->step-src->cols;
400 int cpt_1 = img->cols-src->cols-1;
401 int* imgptr = img->data.i;
405 for ( int i = 0; i < 256; i++ )
408 for ( int i = 0; i < src->cols+2; i++ )
414 uchar* srcptr = src->data.ptr;
418 uchar* maskptr = mask->data.ptr;
419 for ( int i = 0; i < src->rows; i++ )
423 for ( int j = 0; j < src->cols; j++ )
429 *srcptr = 0xff-*srcptr;
430 level_size[*srcptr]++;
431 *imgptr = ((*srcptr>>5)<<8)|(*srcptr);
445 startptr = imgptr+img->cols+1;
446 for ( int i = 0; i < src->rows; i++ )
450 for ( int j = 0; j < src->cols; j++ )
452 *srcptr = 0xff-*srcptr;
453 level_size[*srcptr]++;
454 *imgptr = ((*srcptr>>5)<<8)|(*srcptr);
463 for ( int i = 0; i < src->cols+2; i++ )
470 for ( int i = 1; i < 256; i++ )
472 heap_cur[i] = heap_cur[i-1]+level_size[i-1]+1;
478 static void extractMSER_8UC1_Pass( int* ioptr,
482 MSERGrowHistory* histptr,
483 MSERConnectedComp* comptr,
490 CvMemStorage* storage )
492 comptr->grey_level = 256;
494 comptr->grey_level = (*imgptr)&0xff;
495 initMSERComp( comptr );
496 *imgptr |= 0x80000000;
497 heap_cur += (*imgptr)&0xff;
498 int dir[] = { 1, step, -1, -step };
499 #ifdef __INTRIN_ENABLED__
500 unsigned long heapbit[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
501 unsigned long* bit_cur = heapbit+(((*imgptr)&0x700)>>8);
505 // take tour of all the 4 directions
506 while ( ((*imgptr)&0x70000) < 0x40000 )
509 int* imgptr_nbr = imgptr+dir[((*imgptr)&0x70000)>>16];
510 if ( *imgptr_nbr >= 0 ) // if the neighbor is not visited yet
512 *imgptr_nbr |= 0x80000000; // mark it as visited
513 if ( ((*imgptr_nbr)&0xff) < ((*imgptr)&0xff) )
515 // when the value of neighbor smaller than current
516 // push current to boundary heap and make the neighbor to be the current one
517 // create an empty comp
521 heap_cur += ((*imgptr_nbr)&0xff)-((*imgptr)&0xff);
522 #ifdef __INTRIN_ENABLED__
523 _bitset( bit_cur, (*imgptr)&0x1f );
524 bit_cur += (((*imgptr_nbr)&0x700)-((*imgptr)&0x700))>>8;
528 initMSERComp( comptr );
529 comptr->grey_level = (*imgptr)&0xff;
532 // otherwise, push the neighbor to boundary heap
533 heap_cur[((*imgptr_nbr)&0xff)-((*imgptr)&0xff)]++;
534 *heap_cur[((*imgptr_nbr)&0xff)-((*imgptr)&0xff)] = imgptr_nbr;
535 #ifdef __INTRIN_ENABLED__
536 _bitset( bit_cur+((((*imgptr_nbr)&0x700)-((*imgptr)&0x700))>>8), (*imgptr_nbr)&0x1f );
542 int imsk = (int)(imgptr-ioptr);
543 ptsptr->pt = cvPoint( imsk&stepmask, imsk>>stepgap );
544 // get the current location
545 accumulateMSERComp( comptr, ptsptr );
547 // get the next pixel from boundary heap
552 #ifdef __INTRIN_ENABLED__
554 _bitreset( bit_cur, (*imgptr)&0x1f );
557 #ifdef __INTRIN_ENABLED__
558 bool found_pixel = 0;
559 unsigned long pixel_val;
560 for ( int i = ((*imgptr)&0x700)>>8; i < 8; i++ )
562 if ( _BitScanForward( &pixel_val, *bit_cur ) )
566 heap_cur += pixel_val-((*imgptr)&0xff);
574 unsigned long pixel_val = 0;
575 for ( unsigned long i = ((*imgptr)&0xff)+1; i < 256; i++ )
589 #ifdef __INTRIN_ENABLED__
591 _bitreset( bit_cur, pixel_val&0x1f );
593 if ( pixel_val < comptr[-1].grey_level )
595 // check the stablity and push a new history, increase the grey level
596 if ( MSERStableCheck( comptr, params ) )
598 CvContour* contour = MSERToContour( comptr, storage );
599 contour->color = color;
600 cvSeqPush( contours, &contour );
602 MSERNewHistory( comptr, histptr );
603 comptr[0].grey_level = pixel_val;
606 // keep merging top two comp in stack until the grey level >= pixel_val
610 MSERMergeComp( comptr+1, comptr, comptr, histptr );
612 if ( pixel_val <= comptr[0].grey_level )
614 if ( pixel_val < comptr[-1].grey_level )
616 // check the stablity here otherwise it wouldn't be an ER
617 if ( MSERStableCheck( comptr, params ) )
619 CvContour* contour = MSERToContour( comptr, storage );
620 contour->color = color;
621 cvSeqPush( contours, &contour );
623 MSERNewHistory( comptr, histptr );
624 comptr[0].grey_level = pixel_val;
636 static void extractMSER_8UC1( CvMat* src,
639 CvMemStorage* storage,
644 while ( step < src->step+2 )
649 int stepmask = step-1;
651 // to speedup the process, make the width to be 2^N
652 CvMat* img = cvCreateMat( src->rows+2, step, CV_32SC1 );
653 int* ioptr = img->data.i+step+1;
656 // pre-allocate boundary heap
657 int** heap = (int**)cvAlloc( (src->rows*src->cols+256)*sizeof(heap[0]) );
658 int** heap_start[256];
659 heap_start[0] = heap;
661 // pre-allocate linked point and grow history
662 LinkedPoint* pts = (LinkedPoint*)cvAlloc( src->rows*src->cols*sizeof(pts[0]) );
663 MSERGrowHistory* history = (MSERGrowHistory*)cvAlloc( src->rows*src->cols*sizeof(history[0]) );
664 MSERConnectedComp comp[257];
666 // darker to brighter (MSER-)
667 imgptr = preprocessMSER_8UC1( img, heap_start, src, mask );
668 extractMSER_8UC1_Pass( ioptr, imgptr, heap_start, pts, history, comp, step, stepmask, stepgap, params, -1, contours, storage );
669 // brighter to darker (MSER+)
670 imgptr = preprocessMSER_8UC1( img, heap_start, src, mask );
671 extractMSER_8UC1_Pass( ioptr, imgptr, heap_start, pts, history, comp, step, stepmask, stepgap, params, 1, contours, storage );
677 cvReleaseMat( &img );
686 double m; // the margin used to prune area later
693 // to make the finding of root less painful
696 // a point double-linked list
698 // the temporary msr (set to NULL at every re-initialise)
700 // the global msr (once set, never to NULL)
702 // the index of the node, at this point, it should be x at the first 16-bits, and y at the last 16-bits.
717 static double ChiSquaredDistance( uchar* x, uchar* y )
719 return (double)((x[0]-y[0])*(x[0]-y[0]))/(double)(x[0]+y[0]+1e-10)+
720 (double)((x[1]-y[1])*(x[1]-y[1]))/(double)(x[1]+y[1]+1e-10)+
721 (double)((x[2]-y[2])*(x[2]-y[2]))/(double)(x[2]+y[2]+1e-10);
724 static void initMSCRNode( MSCRNode* node )
726 node->gmsr = node->tmsr = NULL;
727 node->reinit = 0xffff;
729 node->sizei = node->size = 1;
730 node->prev = node->next = node->shortcut = node;
733 // the preprocess to get the edge list with proper gaussian blur
734 static int preprocessMSER_8UC3( MSCRNode* node,
744 int srccpt = src->step-src->cols*3;
745 uchar* srcptr = src->data.ptr;
746 uchar* lastptr = src->data.ptr+3;
747 double* dxptr = dx->data.db;
748 for ( int i = 0; i < src->rows; i++ )
750 for ( int j = 0; j < src->cols-1; j++ )
752 *dxptr = ChiSquaredDistance( srcptr, lastptr );
760 srcptr = src->data.ptr;
761 lastptr = src->data.ptr+src->step;
762 double* dyptr = dy->data.db;
763 for ( int i = 0; i < src->rows-1; i++ )
765 for ( int j = 0; j < src->cols; j++ )
767 *dyptr = ChiSquaredDistance( srcptr, lastptr );
775 // get dx and dy and blur it
776 if ( edgeBlurSize >= 1 )
778 cvSmooth( dx, dx, CV_GAUSSIAN, edgeBlurSize, edgeBlurSize );
779 cvSmooth( dy, dy, CV_GAUSSIAN, edgeBlurSize, edgeBlurSize );
783 // assian dx, dy to proper edge list and initialize mscr node
784 // the nasty code here intended to avoid extra loops
788 int maskcpt = mask->step-mask->cols+1;
789 uchar* maskptr = mask->data.ptr;
790 MSCRNode* nodeptr = node;
791 initMSCRNode( nodeptr );
793 *total += edge->chi = *dxptr;
794 if ( maskptr[0] && maskptr[1] )
796 edge->left = nodeptr;
797 edge->right = nodeptr+1;
804 for ( int i = 1; i < src->cols-1; i++ )
806 initMSCRNode( nodeptr );
808 if ( maskptr[0] && maskptr[1] )
810 *total += edge->chi = *dxptr;
811 edge->left = nodeptr;
812 edge->right = nodeptr+1;
820 initMSCRNode( nodeptr );
821 nodeptr->index = src->cols-1;
824 for ( int i = 1; i < src->rows-1; i++ )
826 initMSCRNode( nodeptr );
827 nodeptr->index = i<<16;
830 if ( maskptr[-mask->step] )
832 *total += edge->chi = *dyptr;
833 edge->left = nodeptr-src->cols;
834 edge->right = nodeptr;
840 *total += edge->chi = *dxptr;
841 edge->left = nodeptr;
842 edge->right = nodeptr+1;
851 for ( int j = 1; j < src->cols-1; j++ )
853 initMSCRNode( nodeptr );
854 nodeptr->index = (i<<16)|j;
857 if ( maskptr[-mask->step] )
859 *total += edge->chi = *dyptr;
860 edge->left = nodeptr-src->cols;
861 edge->right = nodeptr;
867 *total += edge->chi = *dxptr;
868 edge->left = nodeptr;
869 edge->right = nodeptr+1;
879 initMSCRNode( nodeptr );
880 nodeptr->index = (i<<16)|(src->cols-1);
881 if ( maskptr[0] && maskptr[-mask->step] )
883 *total += edge->chi = *dyptr;
884 edge->left = nodeptr-src->cols;
885 edge->right = nodeptr;
893 initMSCRNode( nodeptr );
894 nodeptr->index = (src->rows-1)<<16;
899 *total += edge->chi = *dxptr;
900 edge->left = nodeptr;
901 edge->right = nodeptr+1;
905 if ( maskptr[-mask->step] )
907 *total += edge->chi = *dyptr;
908 edge->left = nodeptr-src->cols;
909 edge->right = nodeptr;
918 for ( int i = 1; i < src->cols-1; i++ )
920 initMSCRNode( nodeptr );
921 nodeptr->index = ((src->rows-1)<<16)|i;
926 *total += edge->chi = *dxptr;
927 edge->left = nodeptr;
928 edge->right = nodeptr+1;
932 if ( maskptr[-mask->step] )
934 *total += edge->chi = *dyptr;
935 edge->left = nodeptr-src->cols;
936 edge->right = nodeptr;
946 initMSCRNode( nodeptr );
947 nodeptr->index = ((src->rows-1)<<16)|(src->cols-1);
948 if ( maskptr[0] && maskptr[-mask->step] )
950 *total += edge->chi = *dyptr;
951 edge->left = nodeptr-src->cols;
952 edge->right = nodeptr;
956 MSCRNode* nodeptr = node;
957 initMSCRNode( nodeptr );
959 *total += edge->chi = *dxptr;
961 edge->left = nodeptr;
962 edge->right = nodeptr+1;
965 for ( int i = 1; i < src->cols-1; i++ )
967 initMSCRNode( nodeptr );
969 *total += edge->chi = *dxptr;
971 edge->left = nodeptr;
972 edge->right = nodeptr+1;
976 initMSCRNode( nodeptr );
977 nodeptr->index = src->cols-1;
979 for ( int i = 1; i < src->rows-1; i++ )
981 initMSCRNode( nodeptr );
982 nodeptr->index = i<<16;
983 *total += edge->chi = *dyptr;
985 edge->left = nodeptr-src->cols;
986 edge->right = nodeptr;
988 *total += edge->chi = *dxptr;
990 edge->left = nodeptr;
991 edge->right = nodeptr+1;
994 for ( int j = 1; j < src->cols-1; j++ )
996 initMSCRNode( nodeptr );
997 nodeptr->index = (i<<16)|j;
998 *total += edge->chi = *dyptr;
1000 edge->left = nodeptr-src->cols;
1001 edge->right = nodeptr;
1003 *total += edge->chi = *dxptr;
1005 edge->left = nodeptr;
1006 edge->right = nodeptr+1;
1010 initMSCRNode( nodeptr );
1011 nodeptr->index = (i<<16)|(src->cols-1);
1012 *total += edge->chi = *dyptr;
1014 edge->left = nodeptr-src->cols;
1015 edge->right = nodeptr;
1019 initMSCRNode( nodeptr );
1020 nodeptr->index = (src->rows-1)<<16;
1021 *total += edge->chi = *dxptr;
1023 edge->left = nodeptr;
1024 edge->right = nodeptr+1;
1026 *total += edge->chi = *dyptr;
1028 edge->left = nodeptr-src->cols;
1029 edge->right = nodeptr;
1032 for ( int i = 1; i < src->cols-1; i++ )
1034 initMSCRNode( nodeptr );
1035 nodeptr->index = ((src->rows-1)<<16)|i;
1036 *total += edge->chi = *dxptr;
1038 edge->left = nodeptr;
1039 edge->right = nodeptr+1;
1041 *total += edge->chi = *dyptr;
1043 edge->left = nodeptr-src->cols;
1044 edge->right = nodeptr;
1048 initMSCRNode( nodeptr );
1049 nodeptr->index = ((src->rows-1)<<16)|(src->cols-1);
1050 *total += edge->chi = *dyptr;
1051 edge->left = nodeptr-src->cols;
1052 edge->right = nodeptr;
1057 #define cmp_mscr_edge(edge1, edge2) \
1058 ((edge1).chi < (edge2).chi)
1060 static CV_IMPLEMENT_QSORT( QuickSortMSCREdge, MSCREdge, cmp_mscr_edge )
1062 // to find the root of one region
1063 static MSCRNode* findMSCR( MSCRNode* x )
1071 if ( next == x ) break;
1080 if ( prev == x ) break;
1086 // the stable mscr should be:
1087 // bigger than minArea and smaller than maxArea
1088 // differ from its ancestor more than minDiversity
1089 static bool MSCRStableCheck( MSCRNode* x, MSERParams params )
1091 if ( x->size <= params.minArea || x->size >= params.maxArea )
1093 if ( x->gmsr == NULL )
1095 double div = (double)(x->size-x->gmsr->size)/(double)x->size;
1096 return div > params.minDiversity;
1100 extractMSER_8UC3( CvMat* src,
1103 CvMemStorage* storage,
1106 MSCRNode* map = (MSCRNode*)cvAlloc( src->cols*src->rows*sizeof(map[0]) );
1107 int Ne = src->cols*src->rows*2-src->cols-src->rows;
1108 MSCREdge* edge = (MSCREdge*)cvAlloc( Ne*sizeof(edge[0]) );
1109 TempMSCR* mscr = (TempMSCR*)cvAlloc( src->cols*src->rows*sizeof(mscr[0]) );
1111 CvMat* dx = cvCreateMat( src->rows, src->cols-1, CV_64FC1 );
1112 CvMat* dy = cvCreateMat( src->rows-1, src->cols, CV_64FC1 );
1113 Ne = preprocessMSER_8UC3( map, edge, &emean, src, mask, dx, dy, Ne, params.edgeBlurSize );
1114 emean = emean / (double)Ne;
1115 QuickSortMSCREdge( edge, Ne, 0 );
1116 MSCREdge* edge_ub = edge+Ne;
1117 MSCREdge* edgeptr = edge;
1118 TempMSCR* mscrptr = mscr;
1119 // the evolution process
1120 for ( int i = 0; i < params.maxEvolution; i++ )
1122 double k = (double)i/(double)params.maxEvolution*(TABLE_SIZE-1);
1123 int ti = cvFloor(k);
1124 double reminder = k-ti;
1125 double thres = emean*(chitab3[ti]*(1-reminder)+chitab3[ti+1]*reminder);
1126 // to process all the edges in the list that chi < thres
1127 while ( edgeptr < edge_ub && edgeptr->chi < thres )
1129 MSCRNode* lr = findMSCR( edgeptr->left );
1130 MSCRNode* rr = findMSCR( edgeptr->right );
1131 // get the region root (who is responsible)
1134 // rank idea take from: N-tree Disjoint-Set Forests for Maximally Stable Extremal Regions
1135 if ( rr->rank > lr->rank )
1138 CV_SWAP( lr, rr, tmp );
1139 } else if ( lr->rank == rr->rank ) {
1140 // at the same rank, we will compare the size
1141 if ( lr->size > rr->size )
1144 CV_SWAP( lr, rr, tmp );
1149 lr->size += rr->size;
1150 // join rr to the end of list lr (lr is a endless double-linked list)
1151 lr->prev->next = rr;
1152 lr->prev = rr->prev;
1153 rr->prev->next = lr;
1155 // area threshold force to reinitialize
1156 if ( lr->size > (lr->size-rr->size)*params.areaThreshold )
1158 lr->sizei = lr->size;
1160 if ( lr->tmsr != NULL )
1162 lr->tmsr->m = lr->dt-lr->di;
1165 lr->di = edgeptr->chi;
1168 lr->dt = edgeptr->chi;
1169 if ( i > lr->reinit )
1171 double s = (double)(lr->size-lr->sizei)/(lr->dt-lr->di);
1174 // skip the first one and check stablity
1175 if ( i > lr->reinit+1 && MSCRStableCheck( lr, params ) )
1177 if ( lr->tmsr == NULL )
1179 lr->gmsr = lr->tmsr = mscrptr;
1182 lr->tmsr->size = lr->size;
1183 lr->tmsr->head = lr;
1184 lr->tmsr->tail = lr->prev;
1193 if ( edgeptr >= edge_ub )
1196 for ( TempMSCR* ptr = mscr; ptr < mscrptr; ptr++ )
1197 // to prune area with margin less than minMargin
1198 if ( ptr->m > params.minMargin )
1200 CvSeq* _contour = cvCreateSeq( CV_SEQ_KIND_GENERIC|CV_32SC2, sizeof(CvContour), sizeof(CvPoint), storage );
1201 cvSeqPushMulti( _contour, 0, ptr->size );
1202 MSCRNode* lpt = ptr->head;
1203 for ( int i = 0; i < ptr->size; i++ )
1205 CvPoint* pt = CV_GET_SEQ_ELEM( CvPoint, _contour, i );
1206 pt->x = (lpt->index)&0xffff;
1207 pt->y = (lpt->index)>>16;
1210 CvContour* contour = (CvContour*)_contour;
1211 cvBoundingRect( contour );
1213 cvSeqPush( contours, &contour );
1215 cvReleaseMat( &dx );
1216 cvReleaseMat( &dy );
1223 extractMSER( CvArr* _img,
1226 CvMemStorage* storage,
1229 CvMat srchdr, *src = cvGetMat( _img, &srchdr );
1230 CvMat maskhdr, *mask = _mask ? cvGetMat( _mask, &maskhdr ) : 0;
1231 CvSeq* contours = 0;
1233 CV_Assert(src != 0);
1234 CV_Assert(CV_MAT_TYPE(src->type) == CV_8UC1 || CV_MAT_TYPE(src->type) == CV_8UC3);
1235 CV_Assert(mask == 0 || (CV_ARE_SIZES_EQ(src, mask) && CV_MAT_TYPE(mask->type) == CV_8UC1));
1236 CV_Assert(storage != 0);
1238 contours = *_contours = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvSeq*), storage );
1240 // choose different method for different image type
1241 // for grey image, it is: Linear Time Maximally Stable Extremal Regions
1242 // for color image, it is: Maximally Stable Colour Regions for Recognition and Matching
1243 switch ( CV_MAT_TYPE(src->type) )
1246 extractMSER_8UC1( src, mask, contours, storage, params );
1249 extractMSER_8UC3( src, mask, contours, storage, params );
1255 MSER::MSER( int _delta, int _min_area, int _max_area,
1256 double _max_variation, double _min_diversity,
1257 int _max_evolution, double _area_threshold,
1258 double _min_margin, int _edge_blur_size )
1259 : delta(_delta), minArea(_min_area), maxArea(_max_area),
1260 maxVariation(_max_variation), minDiversity(_min_diversity),
1261 maxEvolution(_max_evolution), areaThreshold(_area_threshold),
1262 minMargin(_min_margin), edgeBlurSize(_edge_blur_size)
1266 void MSER::operator()( const Mat& image, vector<vector<Point> >& dstcontours, const Mat& mask ) const
1268 CvMat _image = image, _mask, *pmask = 0;
1270 pmask = &(_mask = mask);
1271 MemStorage storage(cvCreateMemStorage(0));
1272 Seq<CvSeq*> contours;
1273 extractMSER( &_image, pmask, &contours.seq, storage,
1274 MSERParams(delta, minArea, maxArea, maxVariation, minDiversity,
1275 maxEvolution, areaThreshold, minMargin, edgeBlurSize));
1276 SeqIterator<CvSeq*> it = contours.begin();
1277 size_t i, ncontours = contours.size();
1278 dstcontours.resize(ncontours);
1279 for( i = 0; i < ncontours; i++, ++it )
1280 Seq<Point>(*it).copyTo(dstcontours[i]);
1284 void MserFeatureDetector::detectImpl( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask ) const
1286 vector<vector<Point> > msers;
1288 (*this)(image, msers, mask);
1290 vector<vector<Point> >::const_iterator contour_it = msers.begin();
1291 Rect r(0, 0, image.cols, image.rows);
1292 for( ; contour_it != msers.end(); ++contour_it )
1294 // TODO check transformation from MSER region to KeyPoint
1295 RotatedRect rect = fitEllipse(Mat(*contour_it));
1296 float diam = sqrt(rect.size.height*rect.size.width);
1298 if( diam > std::numeric_limits<float>::epsilon() && r.contains(rect.center) )
1299 keypoints.push_back( KeyPoint(rect.center, diam) );