cvmser.h文件: #ifndef GUARD_cvmser_h #define GUARD_cvmser_h #include "cv.h" typedef struct CvMSERParams { // delta, in the code, it compares (size_{i}-size_{i-delta})/size_{i-delta} int delta; // prune the area which bigger/smaller than max_area/min_area int max_area; int min_area; // prune the area have simliar size to its children float max_variation; // trace back to cut off mser with diversity < min_diversity float min_diversity; /* the next few params for MSER of color image */ // for color image, the evolution steps int max_evolution; // the area threshold to cause re-initialize double area_threshold; // ignore too small margin double min_margin; // the aperture size for edge blur int edge_blur_size; } CvMSERParams; CvMSERParams cvMSERParams( int delta = 5, int min_area = 60, int max_area = 14400, float max_variation = .25, float min_diversity = .2, int max_evolution = 200, double area_threshold = 1.01, double min_margin = .003, int edge_blur_size = 5 ); void cvExtractMSER( CvArr* _img, CvArr* _mask, CvSeq** contours, CvMemStorage* storage, CvMSERParams params ); #endif cvmser.cpp文件: /* Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * The name of Contributor may not be used to endorse or * promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. * Copyright© 2009, Liu Liu All rights reserved. * * OpenCV functions for MSER extraction * * 1. there are two different implementation of MSER, one for grey image, one for color image * 2. the grey image algorithm is taken from: Linear Time Maximally Stable Extremal Regions; * the paper claims to be faster than union-find method; * it actually get 1.5~2m/s on my centrino L7200 1.2GHz laptop. * 3. the color image algorithm is taken from: Maximally Stable Colour Regions for Recognition and Match; * it should be much slower than grey image method ( 3~4 times ); * the chi_table.h file is taken directly from paper's source code which is distributed under GPL. * 4. though the name is *contours*, the result actually is a list of point set. */ #include "cvmser.h" #include "cxmisc.h" typedef struct CvLinkedPoint { struct CvLinkedPoint* prev; struct CvLinkedPoint* next; CvPoint pt; } CvLinkedPoint; // the history of region grown typedef struct CvMSERGrowHistory { struct CvMSERGrowHistory* shortcut; struct CvMSERGrowHistory* child; int stable; // when it ever stabled before, record the size int val; int size; } CvMSERGrowHistory; typedef struct CvMSERConnectedComp { CvLinkedPoint* head; CvLinkedPoint* tail; CvMSERGrowHistory* history; unsigned long grey_level; int size; int dvar; // the derivative of last var float var; // the current variation (most time is the variation of one-step back) } CvMSERConnectedComp; // Linear Time MSER claims by using bsf can get performance gain, here is the implementation // however it seems that will not do any good in real world test #if 0 #ifdef __GNUC__ inline unsigned char _BitScanForward(unsigned long * Index, unsigned long Mask) { unsigned int EFlags = 0; __asm__ ( "bsf %[Mask], %[Index];" "pushf;" "pop %[EFlags];" : [Index]"=r"(*Index), [EFlags]"=r"(EFlags) : [Mask]"r"(Mask) ); return !(EFlags & 0x40); } #define __INTRIN_ENABLED__ #elif _MSC_BUILD #include <intrin.h> #define __INTRIN_ENABLED__ #endif #ifdef __INTRIN_ENABLED__ inline void _bitset(unsigned long * a, unsigned long b) { *a |= 1<<b; } inline void _bitreset(unsigned long * a, unsigned long b) { *a &= ~(1<<b); } #endif #endif CvMSERParams cvMSERParams( int delta, int min_area, int max_area, float max_variation, float min_diversity, int max_evolution, double area_threshold, double min_margin, int edge_blur_size ) { CvMSERParams params; params.delta = delta; params.min_area = min_area; params.max_area = max_area; params.max_variation = max_variation; params.min_diversity = min_diversity; params.max_evolution = max_evolution; params.area_threshold = area_threshold; params.min_margin = min_margin; params.edge_blur_size = edge_blur_size; return params; } // clear the connected component in stack CV_INLINE static void icvInitMSERComp( CvMSERConnectedComp* comp ) { comp->size = 0; comp->var = 0; comp->dvar = 1; comp->history = NULL; } // add history of size to a connected component CV_INLINE static void icvMSERNewHistory( CvMSERConnectedComp* comp, CvMSERGrowHistory* history ) { history->child = history; if ( NULL == comp->history ) { history->shortcut = history; history->stable = 0; } else { comp->history->child = history; history->shortcut = comp->history->shortcut; history->stable = comp->history->stable; } history->val = comp->grey_level; history->size = comp->size; comp->history = history; } // merging two connected component CV_INLINE static void icvMSERMergeComp( CvMSERConnectedComp* comp1, CvMSERConnectedComp* comp2, CvMSERConnectedComp* comp, CvMSERGrowHistory* history ) { CvLinkedPoint* head; CvLinkedPoint* tail; comp->grey_level = comp2->grey_level; history->child = history; // select the winner by size if ( comp1->size >= comp2->size ) { if ( NULL == comp1->history ) { history->shortcut = history; history->stable = 0; } else { comp1->history->child = history; history->shortcut = comp1->history->shortcut; history->stable = comp1->history->stable; } if ( NULL != comp2->history && comp2->history->stable > history->stable ) history->stable = comp2->history->stable; history->val = comp1->grey_level; history->size = comp1->size; // put comp1 to history comp->var = comp1->var; comp->dvar = comp1->dvar; if ( comp1->size > 0 && comp2->size > 0 ) { comp1->tail->next = comp2->head; comp2->head->prev = comp1->tail; } head = ( comp1->size > 0 ) ? comp1->head : comp2->head; tail = ( comp2->size > 0 ) ? comp2->tail : comp1->tail; // always made the newly added in the last of the pixel list (comp1 ... comp2) } else { if ( NULL == comp2->history ) { history->shortcut = history; history->stable = 0; } else { comp2->history->child = history; history->shortcut = comp2->history->shortcut; history->stable = comp2->history->stable; } if ( NULL != comp1->history && comp1->history->stable > history->stable ) history->stable = comp1->history->stable; history->val = comp2->grey_level; history->size = comp2->size; // put comp2 to history comp->var = comp2->var; comp->dvar = comp2->dvar; if ( comp1->size > 0 && comp2->size > 0 ) { comp2->tail->next = comp1->head; comp1->head->prev = comp2->tail; } head = ( comp2->size > 0 ) ? comp2->head : comp1->head; tail = ( comp1->size > 0 ) ? comp1->tail : comp2->tail; // always made the newly added in the last of the pixel list (comp2 ... comp1) } comp->head = head; comp->tail = tail; comp->history = history; comp->size = comp1->size + comp2->size; } CV_INLINE static float icvMSERVariationCalc( CvMSERConnectedComp* comp, int delta ) { CvMSERGrowHistory* history = comp->history; int val = comp->grey_level; if ( NULL != history ) { CvMSERGrowHistory* shortcut = history->shortcut; while ( shortcut != shortcut->shortcut && shortcut->val + delta > val ) shortcut = shortcut->shortcut; CvMSERGrowHistory* child = shortcut->child; while ( child != child->child && child->val + delta <= val ) { shortcut = child; child = child->child; } // get the position of history where the shortcut->val <= delta+val and shortcut->child->val >= delta+val history->shortcut = shortcut; return (float)(comp->size-shortcut->size)/(float)shortcut->size; // here is a small modification of MSER where cal ||R_{i}-R_{i-delta}||/||R_{i-delta}|| // in standard MSER, cal ||R_{i+delta}-R_{i-delta}||/||R_{i}|| // my calculation is simpler and much easier to implement } return 1.; } CV_INLINE static bool icvMSERStableCheck( CvMSERConnectedComp* comp, CvMSERParams params ) { // tricky part: it actually check the stablity of one-step back if ( comp->history == NULL || comp->history->size <= params.min_area || comp->history->size >= params.max_area ) return 0; float div = (float)(comp->history->size-comp->history->stable)/(float)c