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.
44 #define TABLE_SIZE 400
46 static double chitab3[]={0, 0.0150057, 0.0239478, 0.0315227,
47 0.0383427, 0.0446605, 0.0506115, 0.0562786,
48 0.0617174, 0.0669672, 0.0720573, 0.0770099,
49 0.081843, 0.0865705, 0.0912043, 0.0957541,
50 0.100228, 0.104633, 0.108976, 0.113261,
51 0.117493, 0.121676, 0.125814, 0.12991,
52 0.133967, 0.137987, 0.141974, 0.145929,
53 0.149853, 0.15375, 0.15762, 0.161466,
54 0.165287, 0.169087, 0.172866, 0.176625,
55 0.180365, 0.184088, 0.187794, 0.191483,
56 0.195158, 0.198819, 0.202466, 0.2061,
57 0.209722, 0.213332, 0.216932, 0.220521,
58 0.2241, 0.22767, 0.231231, 0.234783,
59 0.238328, 0.241865, 0.245395, 0.248918,
60 0.252435, 0.255947, 0.259452, 0.262952,
61 0.266448, 0.269939, 0.273425, 0.276908,
62 0.280386, 0.283862, 0.287334, 0.290803,
63 0.29427, 0.297734, 0.301197, 0.304657,
64 0.308115, 0.311573, 0.315028, 0.318483,
65 0.321937, 0.32539, 0.328843, 0.332296,
66 0.335749, 0.339201, 0.342654, 0.346108,
67 0.349562, 0.353017, 0.356473, 0.35993,
68 0.363389, 0.366849, 0.37031, 0.373774,
69 0.377239, 0.380706, 0.384176, 0.387648,
70 0.391123, 0.3946, 0.39808, 0.401563,
71 0.405049, 0.408539, 0.412032, 0.415528,
72 0.419028, 0.422531, 0.426039, 0.429551,
73 0.433066, 0.436586, 0.440111, 0.44364,
74 0.447173, 0.450712, 0.454255, 0.457803,
75 0.461356, 0.464915, 0.468479, 0.472049,
76 0.475624, 0.479205, 0.482792, 0.486384,
77 0.489983, 0.493588, 0.4972, 0.500818,
78 0.504442, 0.508073, 0.511711, 0.515356,
79 0.519008, 0.522667, 0.526334, 0.530008,
80 0.533689, 0.537378, 0.541075, 0.54478,
81 0.548492, 0.552213, 0.555942, 0.55968,
82 0.563425, 0.56718, 0.570943, 0.574715,
83 0.578497, 0.582287, 0.586086, 0.589895,
84 0.593713, 0.597541, 0.601379, 0.605227,
85 0.609084, 0.612952, 0.61683, 0.620718,
86 0.624617, 0.628526, 0.632447, 0.636378,
87 0.64032, 0.644274, 0.648239, 0.652215,
88 0.656203, 0.660203, 0.664215, 0.668238,
89 0.672274, 0.676323, 0.680384, 0.684457,
90 0.688543, 0.692643, 0.696755, 0.700881,
91 0.70502, 0.709172, 0.713339, 0.717519,
92 0.721714, 0.725922, 0.730145, 0.734383,
93 0.738636, 0.742903, 0.747185, 0.751483,
94 0.755796, 0.760125, 0.76447, 0.768831,
95 0.773208, 0.777601, 0.782011, 0.786438,
96 0.790882, 0.795343, 0.799821, 0.804318,
97 0.808831, 0.813363, 0.817913, 0.822482,
98 0.827069, 0.831676, 0.836301, 0.840946,
99 0.84561, 0.850295, 0.854999, 0.859724,
100 0.864469, 0.869235, 0.874022, 0.878831,
101 0.883661, 0.888513, 0.893387, 0.898284,
102 0.903204, 0.908146, 0.913112, 0.918101,
103 0.923114, 0.928152, 0.933214, 0.938301,
104 0.943413, 0.94855, 0.953713, 0.958903,
105 0.964119, 0.969361, 0.974631, 0.979929,
106 0.985254, 0.990608, 0.99599, 1.0014,
107 1.00684, 1.01231, 1.01781, 1.02335,
108 1.02891, 1.0345, 1.04013, 1.04579,
109 1.05148, 1.05721, 1.06296, 1.06876,
110 1.07459, 1.08045, 1.08635, 1.09228,
111 1.09826, 1.10427, 1.11032, 1.1164,
112 1.12253, 1.1287, 1.1349, 1.14115,
113 1.14744, 1.15377, 1.16015, 1.16656,
114 1.17303, 1.17954, 1.18609, 1.19269,
115 1.19934, 1.20603, 1.21278, 1.21958,
116 1.22642, 1.23332, 1.24027, 1.24727,
117 1.25433, 1.26144, 1.26861, 1.27584,
118 1.28312, 1.29047, 1.29787, 1.30534,
119 1.31287, 1.32046, 1.32812, 1.33585,
120 1.34364, 1.3515, 1.35943, 1.36744,
121 1.37551, 1.38367, 1.39189, 1.4002,
122 1.40859, 1.41705, 1.42561, 1.43424,
123 1.44296, 1.45177, 1.46068, 1.46967,
124 1.47876, 1.48795, 1.49723, 1.50662,
125 1.51611, 1.52571, 1.53541, 1.54523,
126 1.55517, 1.56522, 1.57539, 1.58568,
127 1.59611, 1.60666, 1.61735, 1.62817,
128 1.63914, 1.65025, 1.66152, 1.67293,
129 1.68451, 1.69625, 1.70815, 1.72023,
130 1.73249, 1.74494, 1.75757, 1.77041,
131 1.78344, 1.79669, 1.81016, 1.82385,
132 1.83777, 1.85194, 1.86635, 1.88103,
133 1.89598, 1.91121, 1.92674, 1.94257,
134 1.95871, 1.97519, 1.99201, 2.0092,
135 2.02676, 2.04471, 2.06309, 2.08189,
136 2.10115, 2.12089, 2.14114, 2.16192,
137 2.18326, 2.2052, 2.22777, 2.25101,
138 2.27496, 2.29966, 2.32518, 2.35156,
139 2.37886, 2.40717, 2.43655, 2.46709,
140 2.49889, 2.53206, 2.56673, 2.60305,
141 2.64117, 2.6813, 2.72367, 2.76854,
142 2.81623, 2.86714, 2.92173, 2.98059,
143 3.04446, 3.1143, 3.19135, 3.27731,
144 3.37455, 3.48653, 3.61862, 3.77982,
145 3.98692, 4.2776, 4.77167, 133.333 };
147 typedef struct CvLinkedPoint
149 struct CvLinkedPoint* prev;
150 struct CvLinkedPoint* next;
155 // the history of region grown
156 typedef struct CvMSERGrowHistory
158 struct CvMSERGrowHistory* shortcut;
159 struct CvMSERGrowHistory* child;
160 int stable; // when it ever stabled before, record the size
166 typedef struct CvMSERConnectedComp
170 CvMSERGrowHistory* history;
171 unsigned long grey_level;
173 int dvar; // the derivative of last var
174 float var; // the current variation (most time is the variation of one-step back)
178 // Linear Time MSER claims by using bsf can get performance gain, here is the implementation
179 // however it seems that will not do any good in real world test
180 inline void _bitset(unsigned long * a, unsigned long b)
184 inline void _bitreset(unsigned long * a, unsigned long b)
189 CvMSERParams cvMSERParams( int delta, int minArea, int maxArea, float maxVariation, float minDiversity, int maxEvolution, double areaThreshold, double minMargin, int edgeBlurSize )
192 params.delta = delta;
193 params.minArea = minArea;
194 params.maxArea = maxArea;
195 params.maxVariation = maxVariation;
196 params.minDiversity = minDiversity;
197 params.maxEvolution = maxEvolution;
198 params.areaThreshold = areaThreshold;
199 params.minMargin = minMargin;
200 params.edgeBlurSize = edgeBlurSize;
204 // clear the connected component in stack
205 CV_INLINE static void
206 icvInitMSERComp( CvMSERConnectedComp* comp )
211 comp->history = NULL;
214 // add history of size to a connected component
215 CV_INLINE static void
216 icvMSERNewHistory( CvMSERConnectedComp* comp,
217 CvMSERGrowHistory* history )
219 history->child = history;
220 if ( NULL == comp->history )
222 history->shortcut = history;
225 comp->history->child = history;
226 history->shortcut = comp->history->shortcut;
227 history->stable = comp->history->stable;
229 history->val = comp->grey_level;
230 history->size = comp->size;
231 comp->history = history;
234 // merging two connected component
235 CV_INLINE static void
236 icvMSERMergeComp( CvMSERConnectedComp* comp1,
237 CvMSERConnectedComp* comp2,
238 CvMSERConnectedComp* comp,
239 CvMSERGrowHistory* history )
243 comp->grey_level = comp2->grey_level;
244 history->child = history;
245 // select the winner by size
246 if ( comp1->size >= comp2->size )
248 if ( NULL == comp1->history )
250 history->shortcut = history;
253 comp1->history->child = history;
254 history->shortcut = comp1->history->shortcut;
255 history->stable = comp1->history->stable;
257 if ( NULL != comp2->history && comp2->history->stable > history->stable )
258 history->stable = comp2->history->stable;
259 history->val = comp1->grey_level;
260 history->size = comp1->size;
261 // put comp1 to history
262 comp->var = comp1->var;
263 comp->dvar = comp1->dvar;
264 if ( comp1->size > 0 && comp2->size > 0 )
266 comp1->tail->next = comp2->head;
267 comp2->head->prev = comp1->tail;
269 head = ( comp1->size > 0 ) ? comp1->head : comp2->head;
270 tail = ( comp2->size > 0 ) ? comp2->tail : comp1->tail;
271 // always made the newly added in the last of the pixel list (comp1 ... comp2)
273 if ( NULL == comp2->history )
275 history->shortcut = history;
278 comp2->history->child = history;
279 history->shortcut = comp2->history->shortcut;
280 history->stable = comp2->history->stable;
282 if ( NULL != comp1->history && comp1->history->stable > history->stable )
283 history->stable = comp1->history->stable;
284 history->val = comp2->grey_level;
285 history->size = comp2->size;
286 // put comp2 to history
287 comp->var = comp2->var;
288 comp->dvar = comp2->dvar;
289 if ( comp1->size > 0 && comp2->size > 0 )
291 comp2->tail->next = comp1->head;
292 comp1->head->prev = comp2->tail;
294 head = ( comp2->size > 0 ) ? comp2->head : comp1->head;
295 tail = ( comp1->size > 0 ) ? comp1->tail : comp2->tail;
296 // always made the newly added in the last of the pixel list (comp2 ... comp1)
300 comp->history = history;
301 comp->size = comp1->size + comp2->size;
304 CV_INLINE static float
305 icvMSERVariationCalc( CvMSERConnectedComp* comp,
308 CvMSERGrowHistory* history = comp->history;
309 int val = comp->grey_level;
310 if ( NULL != history )
312 CvMSERGrowHistory* shortcut = history->shortcut;
313 while ( shortcut != shortcut->shortcut && shortcut->val + delta > val )
314 shortcut = shortcut->shortcut;
315 CvMSERGrowHistory* child = shortcut->child;
316 while ( child != child->child && child->val + delta <= val )
319 child = child->child;
321 // get the position of history where the shortcut->val <= delta+val and shortcut->child->val >= delta+val
322 history->shortcut = shortcut;
323 return (float)(comp->size-shortcut->size)/(float)shortcut->size;
324 // here is a small modification of MSER where cal ||R_{i}-R_{i-delta}||/||R_{i-delta}||
325 // in standard MSER, cal ||R_{i+delta}-R_{i-delta}||/||R_{i}||
326 // my calculation is simpler and much easier to implement
331 CV_INLINE static bool
332 icvMSERStableCheck( CvMSERConnectedComp* comp,
333 CvMSERParams params )
335 // tricky part: it actually check the stablity of one-step back
336 if ( comp->history == NULL || comp->history->size <= params.minArea || comp->history->size >= params.maxArea )
338 float div = (float)(comp->history->size-comp->history->stable)/(float)comp->history->size;
339 float var = icvMSERVariationCalc( comp, params.delta );
340 int dvar = ( comp->var < var || (unsigned long)(comp->history->val + 1) < comp->grey_level );
341 int stable = ( dvar && !comp->dvar && comp->var < params.maxVariation && div > params.minDiversity );
345 comp->history->stable = comp->history->size;
349 // add a pixel to the pixel list
350 CV_INLINE static void
351 icvAccumulateMSERComp( CvMSERConnectedComp* comp,
352 CvLinkedPoint* point )
354 if ( comp->size > 0 )
356 point->prev = comp->tail;
357 comp->tail->next = point;
368 // convert the point set to CvSeq
369 CV_INLINE static CvContour*
370 icvMSERToContour( CvMSERConnectedComp* comp,
371 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 CvLinkedPoint* 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
395 icvPreprocessMSER_8UC1( CvMat* img,
400 int srccpt = src->step-src->cols;
401 int cpt_1 = img->cols-src->cols-1;
402 int* imgptr = img->data.i;
406 for ( int i = 0; i < 256; i++ )
409 for ( int i = 0; i < src->cols+2; i++ )
415 uchar* srcptr = src->data.ptr;
419 uchar* maskptr = mask->data.ptr;
420 for ( int i = 0; i < src->rows; i++ )
424 for ( int j = 0; j < src->cols; j++ )
430 *srcptr = 0xff-*srcptr;
431 level_size[*srcptr]++;
432 *imgptr = ((*srcptr>>5)<<8)|(*srcptr);
446 startptr = imgptr+img->cols+1;
447 for ( int i = 0; i < src->rows; i++ )
451 for ( int j = 0; j < src->cols; j++ )
453 *srcptr = 0xff-*srcptr;
454 level_size[*srcptr]++;
455 *imgptr = ((*srcptr>>5)<<8)|(*srcptr);
464 for ( int i = 0; i < src->cols+2; i++ )
471 for ( int i = 1; i < 256; i++ )
473 heap_cur[i] = heap_cur[i-1]+level_size[i-1]+1;
480 icvExtractMSER_8UC1_Pass( int* ioptr,
483 CvLinkedPoint* ptsptr,
484 CvMSERGrowHistory* histptr,
485 CvMSERConnectedComp* comptr,
492 CvMemStorage* storage )
494 comptr->grey_level = 256;
496 comptr->grey_level = (*imgptr)&0xff;
497 icvInitMSERComp( comptr );
498 *imgptr |= 0x80000000;
499 heap_cur += (*imgptr)&0xff;
500 int dir[] = { 1, step, -1, -step };
501 #ifdef __INTRIN_ENABLED__
502 unsigned long heapbit[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
503 unsigned long* bit_cur = heapbit+(((*imgptr)&0x700)>>8);
507 // take tour of all the 4 directions
508 while ( ((*imgptr)&0x70000) < 0x40000 )
511 int* imgptr_nbr = imgptr+dir[((*imgptr)&0x70000)>>16];
512 if ( *imgptr_nbr >= 0 ) // if the neighbor is not visited yet
514 *imgptr_nbr |= 0x80000000; // mark it as visited
515 if ( ((*imgptr_nbr)&0xff) < ((*imgptr)&0xff) )
517 // when the value of neighbor smaller than current
518 // push current to boundary heap and make the neighbor to be the current one
519 // create an empty comp
523 heap_cur += ((*imgptr_nbr)&0xff)-((*imgptr)&0xff);
524 #ifdef __INTRIN_ENABLED__
525 _bitset( bit_cur, (*imgptr)&0x1f );
526 bit_cur += (((*imgptr_nbr)&0x700)-((*imgptr)&0x700))>>8;
530 icvInitMSERComp( comptr );
531 comptr->grey_level = (*imgptr)&0xff;
534 // otherwise, push the neighbor to boundary heap
535 heap_cur[((*imgptr_nbr)&0xff)-((*imgptr)&0xff)]++;
536 *heap_cur[((*imgptr_nbr)&0xff)-((*imgptr)&0xff)] = imgptr_nbr;
537 #ifdef __INTRIN_ENABLED__
538 _bitset( bit_cur+((((*imgptr_nbr)&0x700)-((*imgptr)&0x700))>>8), (*imgptr_nbr)&0x1f );
544 int i = (int)(imgptr-ioptr);
545 ptsptr->pt = cvPoint( i&stepmask, i>>stepgap );
546 // get the current location
547 icvAccumulateMSERComp( comptr, ptsptr );
549 // get the next pixel from boundary heap
554 #ifdef __INTRIN_ENABLED__
556 _bitreset( bit_cur, (*imgptr)&0x1f );
559 #ifdef __INTRIN_ENABLED__
560 bool found_pixel = 0;
561 unsigned long pixel_val;
562 for ( int i = ((*imgptr)&0x700)>>8; i < 8; i++ )
564 if ( _BitScanForward( &pixel_val, *bit_cur ) )
568 heap_cur += pixel_val-((*imgptr)&0xff);
576 unsigned long pixel_val = 0;
577 for ( unsigned long i = ((*imgptr)&0xff)+1; i < 256; i++ )
591 #ifdef __INTRIN_ENABLED__
593 _bitreset( bit_cur, pixel_val&0x1f );
595 if ( pixel_val < comptr[-1].grey_level )
597 // check the stablity and push a new history, increase the grey level
598 if ( icvMSERStableCheck( comptr, params ) )
600 CvContour* contour = icvMSERToContour( comptr, storage );
601 contour->color = color;
602 cvSeqPush( contours, &contour );
604 icvMSERNewHistory( comptr, histptr );
605 comptr[0].grey_level = pixel_val;
608 // keep merging top two comp in stack until the grey level >= pixel_val
612 icvMSERMergeComp( comptr+1, comptr, comptr, histptr );
614 if ( pixel_val <= comptr[0].grey_level )
616 if ( pixel_val < comptr[-1].grey_level )
618 // check the stablity here otherwise it wouldn't be an ER
619 if ( icvMSERStableCheck( comptr, params ) )
621 CvContour* contour = icvMSERToContour( comptr, storage );
622 contour->color = color;
623 cvSeqPush( contours, &contour );
625 icvMSERNewHistory( comptr, histptr );
626 comptr[0].grey_level = pixel_val;
639 icvExtractMSER_8UC1( CvMat* src,
642 CvMemStorage* storage,
643 CvMSERParams params )
647 while ( step < src->step+2 )
652 int stepmask = step-1;
654 // to speedup the process, make the width to be 2^N
655 CvMat* img = cvCreateMat( src->rows+2, step, CV_32SC1 );
656 int* ioptr = img->data.i+step+1;
659 // pre-allocate boundary heap
660 int** heap = (int**)cvAlloc( (src->rows*src->cols+256)*sizeof(heap[0]) );
661 int** heap_start[256];
662 heap_start[0] = heap;
664 // pre-allocate linked point and grow history
665 CvLinkedPoint* pts = (CvLinkedPoint*)cvAlloc( src->rows*src->cols*sizeof(pts[0]) );
666 CvMSERGrowHistory* history = (CvMSERGrowHistory*)cvAlloc( src->rows*src->cols*sizeof(history[0]) );
667 CvMSERConnectedComp comp[257];
669 // darker to brighter (MSER-)
670 imgptr = icvPreprocessMSER_8UC1( img, heap_start, src, mask );
671 icvExtractMSER_8UC1_Pass( ioptr, imgptr, heap_start, pts, history, comp, step, stepmask, stepgap, params, -1, contours, storage );
672 // brighter to darker (MSER+)
673 imgptr = icvPreprocessMSER_8UC1( img, heap_start, src, mask );
674 icvExtractMSER_8UC1_Pass( ioptr, imgptr, heap_start, pts, history, comp, step, stepmask, stepgap, params, 1, contours, storage );
680 cvReleaseMat( &img );
685 typedef struct CvTempMSCR
689 double m; // the margin used to prune area later
693 typedef struct CvMSCRNode
695 CvMSCRNode* shortcut;
696 // to make the finding of root less painful
699 // a point double-linked list
701 // the temporary msr (set to NULL at every re-initialise)
703 // the global msr (once set, never to NULL)
705 // the index of the node, at this point, it should be x at the first 16-bits, and y at the last 16-bits.
713 typedef struct CvMSCREdge
720 CV_INLINE static double
721 icvChisquaredDistance( uchar* x, uchar* y )
723 return (double)((x[0]-y[0])*(x[0]-y[0]))/(double)(x[0]+y[0]+1e-10)+
724 (double)((x[1]-y[1])*(x[1]-y[1]))/(double)(x[1]+y[1]+1e-10)+
725 (double)((x[2]-y[2])*(x[2]-y[2]))/(double)(x[2]+y[2]+1e-10);
728 CV_INLINE static void
729 icvInitMSCRNode( CvMSCRNode* node )
731 node->gmsr = node->tmsr = NULL;
732 node->reinit = 0xffff;
734 node->sizei = node->size = 1;
735 node->prev = node->next = node->shortcut = node;
738 // the preprocess to get the edge list with proper gaussian blur
740 icvPreprocessMSER_8UC3( CvMSCRNode* node,
750 int srccpt = src->step-src->cols*3;
751 uchar* srcptr = src->data.ptr;
752 uchar* lastptr = src->data.ptr+3;
753 double* dxptr = dx->data.db;
754 for ( int i = 0; i < src->rows; i++ )
756 for ( int j = 0; j < src->cols-1; j++ )
758 *dxptr = icvChisquaredDistance( srcptr, lastptr );
766 srcptr = src->data.ptr;
767 lastptr = src->data.ptr+src->step;
768 double* dyptr = dy->data.db;
769 for ( int i = 0; i < src->rows-1; i++ )
771 for ( int j = 0; j < src->cols; j++ )
773 *dyptr = icvChisquaredDistance( srcptr, lastptr );
781 // get dx and dy and blur it
782 if ( edgeBlurSize >= 1 )
784 cvSmooth( dx, dx, CV_GAUSSIAN, edgeBlurSize, edgeBlurSize );
785 cvSmooth( dy, dy, CV_GAUSSIAN, edgeBlurSize, edgeBlurSize );
789 // assian dx, dy to proper edge list and initialize mscr node
790 // the nasty code here intended to avoid extra loops
794 int maskcpt = mask->step-mask->cols+1;
795 uchar* maskptr = mask->data.ptr;
796 CvMSCRNode* nodeptr = node;
797 icvInitMSCRNode( nodeptr );
799 *total += edge->chi = *dxptr;
800 if ( maskptr[0] && maskptr[1] )
802 edge->left = nodeptr;
803 edge->right = nodeptr+1;
810 for ( int i = 1; i < src->cols-1; i++ )
812 icvInitMSCRNode( nodeptr );
814 if ( maskptr[0] && maskptr[1] )
816 *total += edge->chi = *dxptr;
817 edge->left = nodeptr;
818 edge->right = nodeptr+1;
826 icvInitMSCRNode( nodeptr );
827 nodeptr->index = src->cols-1;
830 for ( int i = 1; i < src->rows-1; i++ )
832 icvInitMSCRNode( nodeptr );
833 nodeptr->index = i<<16;
836 if ( maskptr[-mask->step] )
838 *total += edge->chi = *dyptr;
839 edge->left = nodeptr-src->cols;
840 edge->right = nodeptr;
846 *total += edge->chi = *dxptr;
847 edge->left = nodeptr;
848 edge->right = nodeptr+1;
857 for ( int j = 1; j < src->cols-1; j++ )
859 icvInitMSCRNode( nodeptr );
860 nodeptr->index = (i<<16)|j;
863 if ( maskptr[-mask->step] )
865 *total += edge->chi = *dyptr;
866 edge->left = nodeptr-src->cols;
867 edge->right = nodeptr;
873 *total += edge->chi = *dxptr;
874 edge->left = nodeptr;
875 edge->right = nodeptr+1;
885 icvInitMSCRNode( nodeptr );
886 nodeptr->index = (i<<16)|(src->cols-1);
887 if ( maskptr[0] && maskptr[-mask->step] )
889 *total += edge->chi = *dyptr;
890 edge->left = nodeptr-src->cols;
891 edge->right = nodeptr;
899 icvInitMSCRNode( nodeptr );
900 nodeptr->index = (src->rows-1)<<16;
905 *total += edge->chi = *dxptr;
906 edge->left = nodeptr;
907 edge->right = nodeptr+1;
911 if ( maskptr[-mask->step] )
913 *total += edge->chi = *dyptr;
914 edge->left = nodeptr-src->cols;
915 edge->right = nodeptr;
924 for ( int i = 1; i < src->cols-1; i++ )
926 icvInitMSCRNode( nodeptr );
927 nodeptr->index = ((src->rows-1)<<16)|i;
932 *total += edge->chi = *dxptr;
933 edge->left = nodeptr;
934 edge->right = nodeptr+1;
938 if ( maskptr[-mask->step] )
940 *total += edge->chi = *dyptr;
941 edge->left = nodeptr-src->cols;
942 edge->right = nodeptr;
952 icvInitMSCRNode( nodeptr );
953 nodeptr->index = ((src->rows-1)<<16)|(src->cols-1);
954 if ( maskptr[0] && maskptr[-mask->step] )
956 *total += edge->chi = *dyptr;
957 edge->left = nodeptr-src->cols;
958 edge->right = nodeptr;
962 CvMSCRNode* nodeptr = node;
963 icvInitMSCRNode( nodeptr );
965 *total += edge->chi = *dxptr;
967 edge->left = nodeptr;
968 edge->right = nodeptr+1;
971 for ( int i = 1; i < src->cols-1; i++ )
973 icvInitMSCRNode( nodeptr );
975 *total += edge->chi = *dxptr;
977 edge->left = nodeptr;
978 edge->right = nodeptr+1;
982 icvInitMSCRNode( nodeptr );
983 nodeptr->index = src->cols-1;
985 for ( int i = 1; i < src->rows-1; i++ )
987 icvInitMSCRNode( nodeptr );
988 nodeptr->index = i<<16;
989 *total += edge->chi = *dyptr;
991 edge->left = nodeptr-src->cols;
992 edge->right = nodeptr;
994 *total += edge->chi = *dxptr;
996 edge->left = nodeptr;
997 edge->right = nodeptr+1;
1000 for ( int j = 1; j < src->cols-1; j++ )
1002 icvInitMSCRNode( nodeptr );
1003 nodeptr->index = (i<<16)|j;
1004 *total += edge->chi = *dyptr;
1006 edge->left = nodeptr-src->cols;
1007 edge->right = nodeptr;
1009 *total += edge->chi = *dxptr;
1011 edge->left = nodeptr;
1012 edge->right = nodeptr+1;
1016 icvInitMSCRNode( nodeptr );
1017 nodeptr->index = (i<<16)|(src->cols-1);
1018 *total += edge->chi = *dyptr;
1020 edge->left = nodeptr-src->cols;
1021 edge->right = nodeptr;
1025 icvInitMSCRNode( nodeptr );
1026 nodeptr->index = (src->rows-1)<<16;
1027 *total += edge->chi = *dxptr;
1029 edge->left = nodeptr;
1030 edge->right = nodeptr+1;
1032 *total += edge->chi = *dyptr;
1034 edge->left = nodeptr-src->cols;
1035 edge->right = nodeptr;
1038 for ( int i = 1; i < src->cols-1; i++ )
1040 icvInitMSCRNode( nodeptr );
1041 nodeptr->index = ((src->rows-1)<<16)|i;
1042 *total += edge->chi = *dxptr;
1044 edge->left = nodeptr;
1045 edge->right = nodeptr+1;
1047 *total += edge->chi = *dyptr;
1049 edge->left = nodeptr-src->cols;
1050 edge->right = nodeptr;
1054 icvInitMSCRNode( nodeptr );
1055 nodeptr->index = ((src->rows-1)<<16)|(src->cols-1);
1056 *total += edge->chi = *dyptr;
1057 edge->left = nodeptr-src->cols;
1058 edge->right = nodeptr;
1063 #define cmp_mscr_edge(edge1, edge2) \
1064 ((edge1).chi < (edge2).chi)
1066 static CV_IMPLEMENT_QSORT( icvQuickSortMSCREdge, CvMSCREdge, cmp_mscr_edge )
1068 // to find the root of one region
1069 CV_INLINE static CvMSCRNode*
1070 icvFindMSCR( CvMSCRNode* x )
1072 CvMSCRNode* prev = x;
1078 if ( next == x ) break;
1082 CvMSCRNode* root = x;
1087 if ( prev == x ) break;
1093 // the stable mscr should be:
1094 // bigger than minArea and smaller than maxArea
1095 // differ from its ancestor more than minDiversity
1096 CV_INLINE static bool
1097 icvMSCRStableCheck( CvMSCRNode* x,
1098 CvMSERParams params )
1100 if ( x->size <= params.minArea || x->size >= params.maxArea )
1102 if ( x->gmsr == NULL )
1104 double div = (double)(x->size-x->gmsr->size)/(double)x->size;
1105 return div > params.minDiversity;
1109 icvExtractMSER_8UC3( CvMat* src,
1112 CvMemStorage* storage,
1113 CvMSERParams params )
1115 CvMSCRNode* map = (CvMSCRNode*)cvAlloc( src->cols*src->rows*sizeof(map[0]) );
1116 int Ne = src->cols*src->rows*2-src->cols-src->rows;
1117 CvMSCREdge* edge = (CvMSCREdge*)cvAlloc( Ne*sizeof(edge[0]) );
1118 CvTempMSCR* mscr = (CvTempMSCR*)cvAlloc( src->cols*src->rows*sizeof(mscr[0]) );
1120 CvMat* dx = cvCreateMat( src->rows, src->cols-1, CV_64FC1 );
1121 CvMat* dy = cvCreateMat( src->rows-1, src->cols, CV_64FC1 );
1122 Ne = icvPreprocessMSER_8UC3( map, edge, &emean, src, mask, dx, dy, Ne, params.edgeBlurSize );
1123 emean = emean / (double)Ne;
1124 icvQuickSortMSCREdge( edge, Ne, 0 );
1125 CvMSCREdge* edge_ub = edge+Ne;
1126 CvMSCREdge* edgeptr = edge;
1127 CvTempMSCR* mscrptr = mscr;
1128 // the evolution process
1129 for ( int i = 0; i < params.maxEvolution; i++ )
1131 double k = (double)i/(double)params.maxEvolution*(TABLE_SIZE-1);
1132 int ti = cvFloor(k);
1133 double reminder = k-ti;
1134 double thres = emean*(chitab3[ti]*(1-reminder)+chitab3[ti+1]*reminder);
1135 // to process all the edges in the list that chi < thres
1136 while ( edgeptr < edge_ub && edgeptr->chi < thres )
1138 CvMSCRNode* lr = icvFindMSCR( edgeptr->left );
1139 CvMSCRNode* rr = icvFindMSCR( edgeptr->right );
1140 // get the region root (who is responsible)
1143 // rank idea take from: N-tree Disjoint-Set Forests for Maximally Stable Extremal Regions
1144 if ( rr->rank > lr->rank )
1147 CV_SWAP( lr, rr, tmp );
1148 } else if ( lr->rank == rr->rank ) {
1149 // at the same rank, we will compare the size
1150 if ( lr->size > rr->size )
1153 CV_SWAP( lr, rr, tmp );
1158 lr->size += rr->size;
1159 // join rr to the end of list lr (lr is a endless double-linked list)
1160 lr->prev->next = rr;
1161 lr->prev = rr->prev;
1162 rr->prev->next = lr;
1164 // area threshold force to reinitialize
1165 if ( lr->size > (lr->size-rr->size)*params.areaThreshold )
1167 lr->sizei = lr->size;
1169 if ( lr->tmsr != NULL )
1171 lr->tmsr->m = lr->dt-lr->di;
1174 lr->di = edgeptr->chi;
1177 lr->dt = edgeptr->chi;
1178 if ( i > lr->reinit )
1180 double s = (double)(lr->size-lr->sizei)/(lr->dt-lr->di);
1183 // skip the first one and check stablity
1184 if ( i > lr->reinit+1 && icvMSCRStableCheck( lr, params ) )
1186 if ( lr->tmsr == NULL )
1188 lr->gmsr = lr->tmsr = mscrptr;
1191 lr->tmsr->size = lr->size;
1192 lr->tmsr->head = lr;
1193 lr->tmsr->tail = lr->prev;
1202 if ( edgeptr >= edge_ub )
1205 for ( CvTempMSCR* ptr = mscr; ptr < mscrptr; ptr++ )
1206 // to prune area with margin less than minMargin
1207 if ( ptr->m > params.minMargin )
1209 CvSeq* _contour = cvCreateSeq( CV_SEQ_KIND_GENERIC|CV_32SC2, sizeof(CvContour), sizeof(CvPoint), storage );
1210 cvSeqPushMulti( _contour, 0, ptr->size );
1211 CvMSCRNode* lpt = ptr->head;
1212 for ( int i = 0; i < ptr->size; i++ )
1214 CvPoint* pt = CV_GET_SEQ_ELEM( CvPoint, _contour, i );
1215 pt->x = (lpt->index)&0xffff;
1216 pt->y = (lpt->index)>>16;
1219 CvContour* contour = (CvContour*)_contour;
1220 cvBoundingRect( contour );
1222 cvSeqPush( contours, &contour );
1224 cvReleaseMat( &dx );
1225 cvReleaseMat( &dy );
1232 cvExtractMSER( CvArr* _img,
1235 CvMemStorage* storage,
1236 CvMSERParams params )
1238 CvMat srchdr, *src = cvGetMat( _img, &srchdr );
1239 CvMat maskhdr, *mask = _mask ? cvGetMat( _mask, &maskhdr ) : 0;
1240 CvSeq* contours = 0;
1242 CV_FUNCNAME( "cvExtractMSER" );
1246 CV_ASSERT(src != 0);
1247 CV_ASSERT(CV_MAT_TYPE(src->type) == CV_8UC1 || CV_MAT_TYPE(src->type) == CV_8UC3);
1248 CV_ASSERT(mask == 0 || (CV_ARE_SIZES_EQ(src, mask) && CV_MAT_TYPE(mask->type) == CV_8UC1));
1249 CV_ASSERT(storage != 0);
1251 contours = *_contours = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvSeq*), storage );
1253 // choose different method for different image type
1254 // for grey image, it is: Linear Time Maximally Stable Extremal Regions
1255 // for color image, it is: Maximally Stable Colour Regions for Recognition and Matching
1256 switch ( CV_MAT_TYPE(src->type) )
1259 icvExtractMSER_8UC1( src, mask, contours, storage, params );
1262 icvExtractMSER_8UC3( src, mask, contours, storage, params );
1275 *(CvMSERParams*)this = cvMSERParams();
1278 MSER::MSER( int _delta, int _min_area, int _max_area,
1279 float _max_variation, float _min_diversity,
1280 int _max_evolution, double _area_threshold,
1281 double _min_margin, int _edge_blur_size )
1283 *(CvMSERParams*)this = cvMSERParams(_delta, _min_area, _max_area, _max_variation,
1284 _min_diversity, _max_evolution, _area_threshold, _min_margin, _edge_blur_size);
1287 void MSER::operator()(Mat& image, vector<vector<Point> >& dstcontours, const Mat& mask) const
1289 CvMat _image = image, _mask, *pmask = 0;
1291 pmask = &(_mask = mask);
1292 MemStorage storage(cvCreateMemStorage(0));
1293 Seq<CvSeq*> contours;
1294 cvExtractMSER( &_image, pmask, &contours.seq, storage, *(const CvMSERParams*)this );
1295 SeqIterator<CvSeq*> it = contours.begin();
1296 size_t i, ncontours = contours.size();
1297 dstcontours.resize(ncontours);
1298 for( i = 0; i < ncontours; i++, ++it )
1299 Seq<Point>(*it).copyTo(dstcontours[i]);