图像局部特征(十五)--MSCR_chi-square 灰度图像-程序员宅基地

技术标签: 图像处理  

原文:

http://blog.csdn.net/zhaocj/article/details/43191829

前面我们介绍了MSER方法,但该方法不适用于对彩色图像的区域检测。为此,Forssen于2007年提出了针对彩色图像的最大稳定极值区域的检测方法——MSCR(Maximally Stable Colour Regions)。

MSCR的检测方法是基于凝聚聚类(AgglomerativeClustering)算法,它把图像中的每个像素作为对象,通过某种相似度准则,依次逐层的进行合并形成簇,即先合并相似度大的对象,再合并相似度小的对象,直到满足某种终止条件为止。这一过程在MSCR中被称为进化过程,即逐步合并图像中的像素,从而形成斑点区域。

MSCR中所使用的相似度准则是卡方距离(Chi-squared distance):

      (1) 

 

 

其中,xy分别为彩色图像中的两个不同像素,下标k表示不同的通道,例如红、绿、蓝三个颜色通道。因此公式1是一种颜色相似度的度量。

 

MSCR通过邻域像素之间的颜色相似度来进行聚类合并,邻域关系可以是水平垂直间邻域,也可以是还包括对角线间邻域。Opencv使用的是水平垂直间邻域,即当前像素与其右侧像素通过公式1得到一个相似度值,再与其下面像素通过公式1得到另一个相似度值。所以一般来说,每个像素都有两个相似度值,但图像的最右侧一列和最下面一行只有一个相似度值。因此对于一个大小为L×M的彩色图像来说,一共有2×L×M-L-M个相似度值。我们把这些相似度值放入一个列表中,由于该相似度是邻域之间的相似度,类似于求图像的边缘,所以该列表也称为边缘列表。

 

在凝聚聚类算法中,是需要逐层进行合并的。在MSCR中合并的层次也称为进化步长,用t来表示,t∈[0…T],根据经验值,T一般为200,即一共进行200步的进化过程。在每一层,都对应一个不同的颜色相似度阈值dthr,在该层只选取那些颜色相似度小于该阈值的像素进行合并。每一层的阈值是不同,并且随着t的增加,阈值也增加,因此达到了合并的区域面积逐步增加的目的。阈值的选取是关键,我们知道,图像像素邻域间的相关性是很大的,也就是通过公式1计算得到的值存在着大量的很小的值,而很大的值少之又少。因此如果我们仍然采用类似于MSER那样,随着t的增加,线性增加dthr的方法,会带来一个严重的后果,就是在进化的开始(t较小的时候),形成斑点区域的速率很快,而在进化的后期(t接近T时),形成斑点区域的速率很慢。为了解决这个问题,即在不同的进化步长下有相同的速率,对于阈值的选取,MSCR采用的是改进型的累积分布函数(CDF)的逆函数的形式。在实际应用中,事先把该函数值存储在表中,使用时通过查表的形式根据不同的t得到不同的dthr。

 

在每一个进化步长内,MSCR会合并一些颜色相似的像素,相邻像素之间就会组成斑点区域,对这些区域我们就需要判断其是否为最大稳定极值区域。对于所形成的斑点区域,我们需要给定该区域的面积a*和相似度阈值d*这两个参数。虽然随着进化步长t的增加,阈值dt(也就是dthr)也在增加,该区域的面积at也在增加,但只有满足两个步长间面积之比大于一定值的时候,才会重新初始化该区域的a*和d*,即:

     (2)

一般athr=1.01。下面给出MSCR判断稳定区域的公式:

    (3)

 

自从上一次初始化(即更新a*和d*)以来,如果s达到了最小值,则该区域为稳定区域。在判断稳定区域的过程中,还应该满足另外两个条件:1是公式3中的t不能是更新a*和d*之后的第一个进化步长;2是公式3中的分母部分要大于一定的阈值,即

 

dt - d* > mmin        (4)

 

一般mmin设置为0.003。稳定区域通过公式3找到后,那么极值区域的判断与MSER的方法一样,是通过稳定区域的面积变量率来判断的,即上一篇文章里的公式1。

 

下面给出彩色图像MSCR的步骤:

 

1、应用公式1计算颜色相似度,得到彩色图像的边缘列表;

 

2、对边缘列表进行平滑处理;

 

3、进化处理,由各个进化步长的距离阈值得到稳定极值区域。

在opencv2.4.9中,MSCR和MSER共用一个类:

 

[cpp] view plain copy print?在CODE上查看代码片派生到我的代码片

  1. class MSER : public CvMSERParams  
  2. {  
  3. public:  
  4. // default constructor  
  5. MSER();  
  6. // constructor that initializes all the algorithm parameters  
  7. MSER( int _delta, int _min_area, int _max_area,  
  8. float _max_variation, float _min_diversity,  
  9. int _max_evolution, double _area_threshold,  
  10. double _min_margin, int _edge_blur_size );  
  11. // runs the extractor on the specified image; returns the MSERs,  
  12. // each encoded as a contour (vector<Point>, see findContours)  
  13. // the optional mask marks the area where MSERs are searched for  
  14. void operator()( const Mat& image, vector<vector<Point> >& msers, const Mat& mask ) const;  
  15. };  

class MSER : public CvMSERParams
{
public:
// default constructor
MSER();
// constructor that initializes all the algorithm parameters
MSER( 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 );
// runs the extractor on the specified image; returns the MSERs,
// each encoded as a contour (vector<Point>, see findContours)
// the optional mask marks the area where MSERs are searched for
void operator()( const Mat& image, vector<vector<Point> >& msers, const Mat& mask ) const;
};

 

 

 

但MSCR比MSER多用了几个参数:

 

_max_evolution为进化总步长,就是参数T,一般T = 200;

 

_area_threshold为重新初始化的面积阈值,就是公式2中的参数athr,一般athr = 1.01;

 

_min_margin为最小步长距离,就是公式4中mmin,一般mmin = 0.003;

 

_edge_blur_size为对边缘列表进行平滑处理的孔径大小

上一篇文件已经介绍过,在MSER类中的重载( )运算符中,调用了extractMSER函数,在该函数内通过判断输入图像的类型确定是灰度图像还是彩色图像,如果是彩色图像则调用extractMSER_8UC3函数:

 

 

[cpp] view plain copy print?在CODE上查看代码片派生到我的代码片

  1. static void  
  2. extractMSER_8UC3( CvMat* src,  
  3.              CvMat* mask,  
  4.              CvSeq* contours,  
  5.              CvMemStorage* storage,  
  6.              MSERParams params )  
  7. {  
  8.     //在应用凝聚聚类算法时,把图像中的每个像素作为一个对象,即一个节点,因此该语句是定义并分配图像节点空间  
  9.     MSCRNode* map = (MSCRNode*)cvAlloc( src->cols*src->rows*sizeof(map[0]) );  
  10.     //定义边缘列表的个数,即2 × L × M – L - M  
  11.     int Ne = src->cols*src->rows*2-src->cols-src->rows;  
  12.     //定义并分配边缘列表空间  
  13.     MSCREdge* edge = (MSCREdge*)cvAlloc( Ne*sizeof(edge[0]) );  
  14.     TempMSCR* mscr = (TempMSCR*)cvAlloc( src->cols*src->rows*sizeof(mscr[0]) );  
  15.     //定义变量,用于由公式1计算图像每个像素颜色相似度的距离均值  
  16.     double emean = 0;  
  17.     //创建水平梯度矩阵,即当前像素与其右侧像素之间的差值  
  18.     CvMat* dx = cvCreateMat( src->rows, src->cols-1, CV_64FC1 );  
  19.     //创建垂直梯度矩阵,即当前像素与其下面像素之间的差值  
  20.     CvMat* dy = cvCreateMat( src->rows-1, src->cols, CV_64FC1 );  
  21.     //MSCR的预处理过程,主要完成步骤1和步骤2,后面会详细讲解  
  22.     Ne = preprocessMSER_8UC3( map, edge, &emean, src, mask, dx, dy, Ne, params.edgeBlurSize );  
  23.     //得到颜色相似度的距离均值  
  24.     emean = emean / (double)Ne;  
  25.     //对边缘列表进行升序排列,便于后面的距离阈值比较  
  26.     QuickSortMSCREdge( edge, Ne, 0 );  
  27.     //定义边缘列表的空间的上限  
  28.     MSCREdge* edge_ub = edge+Ne;  
  29.     //定义边缘列表的地址指针  
  30.     MSCREdge* edgeptr = edge;  
  31.     TempMSCR* mscrptr = mscr;  
  32.     // the evolution process  
  33.     //步骤3,进化处理,在t∈[ 0…T ]中循环,这里的i就是前面文章介绍的进化步长t  
  34.     for ( int i = 0; i < params.maxEvolution; i++ )  
  35.     {  
  36.         //下面的4条语句用于计算当前t下的dthr值,thres为dthr  
  37.         //数组chitab为事先计算好的查询表  
  38.         double k = (double)i/(double)params.maxEvolution*(TABLE_SIZE-1);  
  39.         int ti = cvFloor(k);  
  40.         double reminder = k-ti;  
  41.         double thres = emean*(chitab3[ti]*(1-reminder)+chitab3[ti+1]*reminder);  
  42.         // to process all the edges in the list that chi < thres  
  43.         //处理所有颜色相似度小于阈值的像素  
  44.         //edgeptr < edge_ub的作用是判断边缘列表指针是否超过了列表的上限,即所指向的不是边缘  
  45.         while ( edgeptr < edge_ub && edgeptr->chi < thres )  
  46.         {  
  47.             //由当前像素的左侧像素找到该像素所在的簇的根节点,也就是找到代表该像素所在区域的像素  
  48.             MSCRNode* lr = findMSCR( edgeptr->left );  
  49.             //由当前像素的右侧像素找到该像素所在的簇的根节点,也就是找到代表该像素所在区域的像素  
  50.             //需要注意的是,这里的左侧和右侧并不是真正意义的左侧和右侧,它们是由preprocessMSER_8UC3函数确定的  
  51.             MSCRNode* rr = findMSCR( edgeptr->right );  
  52.             // get the region root (who is responsible)  
  53.             //如果上面得到的两个根节点是一个节点,则不需要进行任何处理  
  54.             //如果这两个根节点不是一个,则需要把它们所代表的两个区域进行合并  
  55.             if ( lr != rr )  
  56.             {  
  57.                 // rank idea take from: N-tree Disjoint-Set Forests for Maximally Stable Extremal Regions  
  58.                 //下面的if语句用于判断是用rr还是用lr来代表合并后的区域,并且最终通过交换来实现lr代表合并后的区域  
  59.                 //rank值大的根节点代表合并后的区域  
  60.                 if ( rr->rank > lr->rank )  
  61.                 {  
  62.                     MSCRNode* tmp;  
  63.                     CV_SWAP( lr, rr, tmp );  
  64.                 } else if ( lr->rank == rr->rank ) {  
  65.                     // at the same rank, we will compare the size  
  66.                     //如果两个根节点的rank值相同,则区域面积大的代表合并后的区域  
  67.                     if ( lr->size > rr->size )  
  68.                     {  
  69.                         MSCRNode* tmp;  
  70.                         CV_SWAP( lr, rr, tmp );  
  71.                     }  
  72.                     lr->rank++;  
  73.                 }  
  74.                 //定义rr所表示的区域的根节点为lr  
  75.                 rr->shortcut = lr;  
  76.                 //合并两个区域,合并后区域面积为两个区域面积之和  
  77.                 lr->size += rr->size;  
  78.                 // join rr to the end of list lr (lr is a endless double-linked list)  
  79.                 //把rr加入lr列表中,组成一个循环双链接列表  
  80.                 lr->prev->next = rr;  
  81.                 lr->prev = rr->prev;  
  82.                 rr->prev->next = lr;  
  83.                 rr->prev = lr;  
  84.                 // area threshold force to reinitialize  
  85.                 //利用公式2计算是否需要区域的重新初始化  
  86.                 //if语句成立,则表示需要重新初始化  
  87.                 if ( lr->size > (lr->size-rr->size)*params.areaThreshold )  
  88.                 {  
  89.                     //更新面积,即a*值  
  90.                     lr->sizei = lr->size;  
  91.                     //更新当前的进化步长,即t值,以区分各个层  
  92.                     lr->reinit = i;  
  93.                     //tmsr保存着上一次计算得到的稳定区域信息  
  94.                     if ( lr->tmsr != NULL )  
  95.                     {  
  96.                         //公式4  
  97.                         lr->tmsr->m = lr->dt-lr->di;  
  98.                         /*tmsr赋值为NULL,表示该区域已经进行了重新初始化,因此在下次进化步长并计算到该节点的时候,需要保存该区域的最大稳定极值区域;还有一个目的是避免重复计算公式4*/  
  99.                         lr->tmsr = NULL;  
  100.                     }  
  101.                     //更新颜色相似度值,即d*值  
  102.                     lr->di = edgeptr->chi;  
  103.                     //为公式3中的s赋予一个极小的值  
  104.                     lr->s = 1e10;  
  105.                 }  
  106.                 //为该区域的颜色相似度赋值  
  107.                 lr->dt = edgeptr->chi;  
  108.                 //在重新初始化以后的进化步长中,当计算到该节点时,需要进入if语句内,以判断最大稳定极值区域  
  109.                 if ( i > lr->reinit )  
  110.                 {  
  111.                     //公式3  
  112.                     double s = (double)(lr->size-lr->sizei)/(lr->dt-lr->di);  
  113.                     //当公式3中的s是最小值时  
  114.                     if ( s < lr->s )  
  115.                     {  
  116.                         // skip the first one and check stablity  
  117.                         // i > lr->reinit+1的目的是避免计算重新初始化后的第一个进化步长  
  118.                         // MSCRStableCheck函数为计算最大稳定机制区域,即计算区域面积的变化率  
  119.                         if ( i > lr->reinit+1 && MSCRStableCheck( lr, params ) )  
  120.                         {  
  121.                             //tmsr为NULL,表示至从上次重新初始化以来,还没有为tmsr赋值,因此这次得到的稳定区域要作为最终输出保存下来  
  122.                             if ( lr->tmsr == NULL )  
  123.                             {  
  124.                                 //gmsr为全局稳定区域,tmsr为暂存稳定区域,mscrptr为mscr的指针变量,它是最终输出的稳定区域  
  125.                                 lr->gmsr = lr->tmsr = mscrptr;  
  126.                                 mscrptr++;    //指向下一个地址  
  127.                             }  
  128.                             //为tmsr赋值  
  129.                             lr->tmsr->size = lr->size;  
  130.                             lr->tmsr->head = lr;  
  131.                             lr->tmsr->tail = lr->prev;  
  132.                             lr->tmsr->m = 0;  
  133.                         }  
  134.                         //保证s为最小值  
  135.                         lr->s = s;  
  136.                     }  
  137.                 }  
  138.             }  
  139.             //指向下一个边缘  
  140.             edgeptr++;  
  141.         }  
  142.         //如果超出了边缘列表的范围,则退出for循环  
  143.         if ( edgeptr >= edge_ub )  
  144.             break;  
  145.     }  
  146.     //对最终得到的稳定区域进行裁剪,并输出  
  147.     for ( TempMSCR* ptr = mscr; ptr < mscrptr; ptr++ )  
  148.         // to prune area with margin less than minMargin  
  149.         //公式4,判断是否满足条件  
  150.         if ( ptr->m > params.minMargin )  
  151.         {  
  152.             //创建序列  
  153.             CvSeq* _contour = cvCreateSeq( CV_SEQ_KIND_GENERIC|CV_32SC2, sizeof(CvContour), sizeof(CvPoint), storage );  
  154.             //初始化该序列  
  155.             cvSeqPushMulti( _contour, 0, ptr->size );  
  156.             MSCRNode* lpt = ptr->head;  
  157.             for ( int i = 0; i < ptr->size; i++ )  
  158.             {  
  159.                 CvPoint* pt = CV_GET_SEQ_ELEM( CvPoint, _contour, i );  
  160.                 //得到稳定区域的坐标值  
  161.                 pt->x = (lpt->index)&0xffff;  
  162.                 pt->y = (lpt->index)>>16;  
  163.                 lpt = lpt->next;  
  164.             }  
  165.             CvContour* contour = (CvContour*)_contour;  
  166.             cvBoundingRect( contour );  
  167.             contour->color = 0;  
  168.             //把坐标值压入序列中  
  169.             cvSeqPush( contours, &contour );  
  170.         }  
  171.     //清内存  
  172.     cvReleaseMat( &dx );  
  173.     cvReleaseMat( &dy );  
  174.     cvFree( &mscr );  
  175.     cvFree( &edge );  
  176.     cvFree( &map );  
  177. }  

static void
extractMSER_8UC3( CvMat* src,
             CvMat* mask,
             CvSeq* contours,
             CvMemStorage* storage,
             MSERParams params )
{
    //在应用凝聚聚类算法时,把图像中的每个像素作为一个对象,即一个节点,因此该语句是定义并分配图像节点空间
    MSCRNode* map = (MSCRNode*)cvAlloc( src->cols*src->rows*sizeof(map[0]) );
    //定义边缘列表的个数,即2 × L × M – L - M
    int Ne = src->cols*src->rows*2-src->cols-src->rows;
    //定义并分配边缘列表空间
    MSCREdge* edge = (MSCREdge*)cvAlloc( Ne*sizeof(edge[0]) );
    TempMSCR* mscr = (TempMSCR*)cvAlloc( src->cols*src->rows*sizeof(mscr[0]) );
    //定义变量,用于由公式1计算图像每个像素颜色相似度的距离均值
    double emean = 0;
    //创建水平梯度矩阵,即当前像素与其右侧像素之间的差值
    CvMat* dx = cvCreateMat( src->rows, src->cols-1, CV_64FC1 );
    //创建垂直梯度矩阵,即当前像素与其下面像素之间的差值
    CvMat* dy = cvCreateMat( src->rows-1, src->cols, CV_64FC1 );
    //MSCR的预处理过程,主要完成步骤1和步骤2,后面会详细讲解
    Ne = preprocessMSER_8UC3( map, edge, &emean, src, mask, dx, dy, Ne, params.edgeBlurSize );
    //得到颜色相似度的距离均值
    emean = emean / (double)Ne;
    //对边缘列表进行升序排列,便于后面的距离阈值比较
    QuickSortMSCREdge( edge, Ne, 0 );
    //定义边缘列表的空间的上限
    MSCREdge* edge_ub = edge+Ne;
    //定义边缘列表的地址指针
    MSCREdge* edgeptr = edge;
    TempMSCR* mscrptr = mscr;
    // the evolution process
    //步骤3,进化处理,在t∈[ 0…T ]中循环,这里的i就是前面文章介绍的进化步长t
    for ( int i = 0; i < params.maxEvolution; i++ )
    {
        //下面的4条语句用于计算当前t下的dthr值,thres为dthr
        //数组chitab为事先计算好的查询表
        double k = (double)i/(double)params.maxEvolution*(TABLE_SIZE-1);
        int ti = cvFloor(k);
        double reminder = k-ti;
        double thres = emean*(chitab3[ti]*(1-reminder)+chitab3[ti+1]*reminder);
        // to process all the edges in the list that chi < thres
        //处理所有颜色相似度小于阈值的像素
        //edgeptr < edge_ub的作用是判断边缘列表指针是否超过了列表的上限,即所指向的不是边缘
        while ( edgeptr < edge_ub && edgeptr->chi < thres )
        {
            //由当前像素的左侧像素找到该像素所在的簇的根节点,也就是找到代表该像素所在区域的像素
            MSCRNode* lr = findMSCR( edgeptr->left );
            //由当前像素的右侧像素找到该像素所在的簇的根节点,也就是找到代表该像素所在区域的像素
            //需要注意的是,这里的左侧和右侧并不是真正意义的左侧和右侧,它们是由preprocessMSER_8UC3函数确定的
            MSCRNode* rr = findMSCR( edgeptr->right );
            // get the region root (who is responsible)
            //如果上面得到的两个根节点是一个节点,则不需要进行任何处理
            //如果这两个根节点不是一个,则需要把它们所代表的两个区域进行合并
            if ( lr != rr )
            {
                // rank idea take from: N-tree Disjoint-Set Forests for Maximally Stable Extremal Regions
                //下面的if语句用于判断是用rr还是用lr来代表合并后的区域,并且最终通过交换来实现lr代表合并后的区域
                //rank值大的根节点代表合并后的区域
                if ( rr->rank > lr->rank )
                {
                    MSCRNode* tmp;
                    CV_SWAP( lr, rr, tmp );
                } else if ( lr->rank == rr->rank ) {
                    // at the same rank, we will compare the size
                    //如果两个根节点的rank值相同,则区域面积大的代表合并后的区域
                    if ( lr->size > rr->size )
                    {
                        MSCRNode* tmp;
                        CV_SWAP( lr, rr, tmp );
                    }
                    lr->rank++;
                }
                //定义rr所表示的区域的根节点为lr
                rr->shortcut = lr;
                //合并两个区域,合并后区域面积为两个区域面积之和
                lr->size += rr->size;
                // join rr to the end of list lr (lr is a endless double-linked list)
                //把rr加入lr列表中,组成一个循环双链接列表
                lr->prev->next = rr;
                lr->prev = rr->prev;
                rr->prev->next = lr;
                rr->prev = lr;
                // area threshold force to reinitialize
                //利用公式2计算是否需要区域的重新初始化
                //if语句成立,则表示需要重新初始化
                if ( lr->size > (lr->size-rr->size)*params.areaThreshold )
                {
                    //更新面积,即a*值
                    lr->sizei = lr->size;
                    //更新当前的进化步长,即t值,以区分各个层
                    lr->reinit = i;
                    //tmsr保存着上一次计算得到的稳定区域信息
                    if ( lr->tmsr != NULL )
                    {
                        //公式4
                        lr->tmsr->m = lr->dt-lr->di;
                        /*tmsr赋值为NULL,表示该区域已经进行了重新初始化,因此在下次进化步长并计算到该节点的时候,需要保存该区域的最大稳定极值区域;还有一个目的是避免重复计算公式4*/
                        lr->tmsr = NULL;
                    }
                    //更新颜色相似度值,即d*值
                    lr->di = edgeptr->chi;
                    //为公式3中的s赋予一个极小的值
                    lr->s = 1e10;
                }
                //为该区域的颜色相似度赋值
                lr->dt = edgeptr->chi;
                //在重新初始化以后的进化步长中,当计算到该节点时,需要进入if语句内,以判断最大稳定极值区域
                if ( i > lr->reinit )
                {
                    //公式3
                    double s = (double)(lr->size-lr->sizei)/(lr->dt-lr->di);
                    //当公式3中的s是最小值时
                    if ( s < lr->s )
                    {
                        // skip the first one and check stablity
                        // i > lr->reinit+1的目的是避免计算重新初始化后的第一个进化步长
                        // MSCRStableCheck函数为计算最大稳定机制区域,即计算区域面积的变化率
                        if ( i > lr->reinit+1 && MSCRStableCheck( lr, params ) )
                        {
                            //tmsr为NULL,表示至从上次重新初始化以来,还没有为tmsr赋值,因此这次得到的稳定区域要作为最终输出保存下来
                            if ( lr->tmsr == NULL )
                            {
                                //gmsr为全局稳定区域,tmsr为暂存稳定区域,mscrptr为mscr的指针变量,它是最终输出的稳定区域
                                lr->gmsr = lr->tmsr = mscrptr;
                                mscrptr++;    //指向下一个地址
                            }
                            //为tmsr赋值
                            lr->tmsr->size = lr->size;
                            lr->tmsr->head = lr;
                            lr->tmsr->tail = lr->prev;
                            lr->tmsr->m = 0;
                        }
                        //保证s为最小值
                        lr->s = s;
                    }
                }
            }
            //指向下一个边缘
            edgeptr++;
        }
        //如果超出了边缘列表的范围,则退出for循环
        if ( edgeptr >= edge_ub )
            break;
    }
    //对最终得到的稳定区域进行裁剪,并输出
    for ( TempMSCR* ptr = mscr; ptr < mscrptr; ptr++ )
        // to prune area with margin less than minMargin
        //公式4,判断是否满足条件
        if ( ptr->m > params.minMargin )
        {
            //创建序列
            CvSeq* _contour = cvCreateSeq( CV_SEQ_KIND_GENERIC|CV_32SC2, sizeof(CvContour), sizeof(CvPoint), storage );
            //初始化该序列
            cvSeqPushMulti( _contour, 0, ptr->size );
            MSCRNode* lpt = ptr->head;
            for ( int i = 0; i < ptr->size; i++ )
            {
                CvPoint* pt = CV_GET_SEQ_ELEM( CvPoint, _contour, i );
                //得到稳定区域的坐标值
                pt->x = (lpt->index)&0xffff;
                pt->y = (lpt->index)>>16;
                lpt = lpt->next;
            }
            CvContour* contour = (CvContour*)_contour;
            cvBoundingRect( contour );
            contour->color = 0;
            //把坐标值压入序列中
            cvSeqPush( contours, &contour );
        }
    //清内存
    cvReleaseMat( &dx );
    cvReleaseMat( &dy );
    cvFree( &mscr );
    cvFree( &edge );
    cvFree( &map );
}

 

 

 

下面我们来介绍一下preprocessMSER_8UC3函数:

 

[cpp] view plain copy print?在CODE上查看代码片派生到我的代码片

  1. // the preprocess to get the edge list with proper gaussian blur  
  2. static int preprocessMSER_8UC3( MSCRNode* node,    //图像像素节点  
  3.             MSCREdge* edge,    //边缘列表  
  4.             double* total,    //求相似度均值时使用,这里是所有像素相似度之和  
  5.             CvMat* src,    //原始图像  
  6.             CvMat* mask,    //掩码矩阵  
  7.             CvMat* dx,    //水平梯度矩阵  
  8.             CvMat* dy,    //垂直梯度矩阵  
  9.             int Ne,    //边缘列表元素的个数  
  10.             int edgeBlurSize )    //平滑处理的孔径尺寸大小  
  11. {  
  12.     int srccpt = src->step-src->cols*3;  
  13.     uchar* srcptr = src->data.ptr;    //图像当前像素指针  
  14.     uchar* lastptr = src->data.ptr+3;    //右侧像素指针  
  15.     double* dxptr = dx->data.db;    //水平梯度数据指针  
  16.     //计算当前像素与其右侧像素之间的颜色相似度  
  17.     for ( int i = 0; i < src->rows; i++ )  
  18.     {  
  19.         //图像最右侧一列没有该相似度,因此j < src->cols-1  
  20.         for ( int j = 0; j < src->cols-1; j++ )  
  21.         {  
  22.             //公式1,计算卡方距离,保存到dx内  
  23.             *dxptr = ChiSquaredDistance( srcptr, lastptr );  
  24.             //地址递增  
  25.             dxptr++;  
  26.             srcptr += 3;  
  27.             lastptr += 3;  
  28.         }  
  29.         //指向下一行  
  30.         srcptr += srccpt+3;  
  31.         lastptr += srccpt+3;  
  32.     }  
  33.     srcptr = src->data.ptr;    //图像当前像素指针  
  34.     lastptr = src->data.ptr+src->step;    //下一行像素指针  
  35.     double* dyptr = dy->data.db;    //垂直梯度数据指针  
  36.     //计算当前像素与其下面一行像素之间的颜色相似度  
  37.     //图像最下面一行没有该相似度,因此i < src->rows-1  
  38.     for ( int i = 0; i < src->rows-1; i++ )  
  39.     {  
  40.         for ( int j = 0; j < src->cols; j++ )  
  41.         {  
  42.             //保存到dy内  
  43.             *dyptr = ChiSquaredDistance( srcptr, lastptr );  
  44.             dyptr++;  
  45.             srcptr += 3;  
  46.             lastptr += 3;  
  47.         }  
  48.         srcptr += srccpt;  
  49.         lastptr += srccpt;  
  50.     }  
  51.     // get dx and dy and blur it  
  52.     //对颜色相似度值进行高斯平滑处理  
  53.     if ( edgeBlurSize >= 1 )  
  54.     {  
  55.         cvSmooth( dx, dx, CV_GAUSSIAN, edgeBlurSize, edgeBlurSize );  
  56.         cvSmooth( dy, dy, CV_GAUSSIAN, edgeBlurSize, edgeBlurSize );  
  57.     }  
  58.     dxptr = dx->data.db;  
  59.     dyptr = dy->data.db;  
  60.     // assian dx, dy to proper edge list and initialize mscr node  
  61.     // the nasty code here intended to avoid extra loops  
  62.     /*下面的if语句是为边缘列表赋值,如果定义了掩码矩阵,则边缘列表不保存被掩码掉的像素的边缘信息,因此边缘列表的个数Ne需要重新计算并输出。在这里我们以没有定义掩码矩阵为例进行讲解,两者的本质是一样的*/  
  63.     if ( mask )  
  64.     {  
  65.         Ne = 0;  
  66.         int maskcpt = mask->step-mask->cols+1;  
  67.         uchar* maskptr = mask->data.ptr;  
  68.         MSCRNode* nodeptr = node;  
  69.         initMSCRNode( nodeptr );  
  70.         nodeptr->index = 0;  
  71.         *total += edge->chi = *dxptr;  
  72.         if ( maskptr[0] && maskptr[1] )  
  73.         {  
  74.             edge->left = nodeptr;  
  75.             edge->right = nodeptr+1;  
  76.             edge++;  
  77.             Ne++;  
  78.         }  
  79.         dxptr++;  
  80.         nodeptr++;  
  81.         maskptr++;  
  82.         for ( int i = 1; i < src->cols-1; i++ )  
  83.         {  
  84.             initMSCRNode( nodeptr );  
  85.             nodeptr->index = i;  
  86.             if ( maskptr[0] && maskptr[1] )  
  87.             {  
  88.                 *total += edge->chi = *dxptr;  
  89.                 edge->left = nodeptr;  
  90.                 edge->right = nodeptr+1;  
  91.                 edge++;  
  92.                 Ne++;  
  93.             }  
  94.             dxptr++;  
  95.             nodeptr++;  
  96.             maskptr++;  
  97.         }  
  98.         initMSCRNode( nodeptr );  
  99.         nodeptr->index = src->cols-1;  
  100.         nodeptr++;  
  101.         maskptr += maskcpt;  
  102.         for ( int i = 1; i < src->rows-1; i++ )  
  103.         {  
  104.             initMSCRNode( nodeptr );  
  105.             nodeptr->index = i<<16;  
  106.             if ( maskptr[0] )  
  107.             {  
  108.                 if ( maskptr[-mask->step] )  
  109.                 {  
  110.                     *total += edge->chi = *dyptr;  
  111.                     edge->left = nodeptr-src->cols;  
  112.                     edge->right = nodeptr;  
  113.                     edge++;  
  114.                     Ne++;  
  115.                 }  
  116.                 if ( maskptr[1] )  
  117.                 {  
  118.                     *total += edge->chi = *dxptr;  
  119.                     edge->left = nodeptr;  
  120.                     edge->right = nodeptr+1;  
  121.                     edge++;  
  122.                     Ne++;  
  123.                 }  
  124.             }  
  125.             dyptr++;  
  126.             dxptr++;  
  127.             nodeptr++;  
  128.             maskptr++;  
  129.             for ( int j = 1; j < src->cols-1; j++ )  
  130.             {  
  131.                 initMSCRNode( nodeptr );  
  132.                 nodeptr->index = (i<<16)|j;  
  133.                 if ( maskptr[0] )  
  134.                 {  
  135.                     if ( maskptr[-mask->step] )  
  136.                     {  
  137.                         *total += edge->chi = *dyptr;  
  138.                         edge->left = nodeptr-src->cols;  
  139.                         edge->right = nodeptr;  
  140.                         edge++;  
  141.                         Ne++;  
  142.                     }  
  143.                     if ( maskptr[1] )  
  144.                     {  
  145.                         *total += edge->chi = *dxptr;  
  146.                         edge->left = nodeptr;  
  147.                         edge->right = nodeptr+1;  
  148.                         edge++;  
  149.                         Ne++;  
  150.                     }  
  151.                 }  
  152.                 dyptr++;  
  153.                 dxptr++;  
  154.                 nodeptr++;  
  155.                 maskptr++;  
  156.             }  
  157.             initMSCRNode( nodeptr );  
  158.             nodeptr->index = (i<<16)|(src->cols-1);  
  159.             if ( maskptr[0] && maskptr[-mask->step] )  
  160.             {  
  161.                 *total += edge->chi = *dyptr;  
  162.                 edge->left = nodeptr-src->cols;  
  163.                 edge->right = nodeptr;  
  164.                 edge++;  
  165.                 Ne++;  
  166.             }  
  167.             dyptr++;  
  168.             nodeptr++;  
  169.             maskptr += maskcpt;  
  170.         }  
  171.         initMSCRNode( nodeptr );  
  172.         nodeptr->index = (src->rows-1)<<16;  
  173.         if ( maskptr[0] )  
  174.         {  
  175.             if ( maskptr[1] )  
  176.             {  
  177.                 *total += edge->chi = *dxptr;  
  178.                 edge->left = nodeptr;  
  179.                 edge->right = nodeptr+1;  
  180.                 edge++;  
  181.                 Ne++;  
  182.             }  
  183.             if ( maskptr[-mask->step] )  
  184.             {  
  185.                 *total += edge->chi = *dyptr;  
  186.                 edge->left = nodeptr-src->cols;  
  187.                 edge->right = nodeptr;  
  188.                 edge++;  
  189.                 Ne++;  
  190.             }  
  191.         }  
  192.         dxptr++;  
  193.         dyptr++;  
  194.         nodeptr++;  
  195.         maskptr++;  
  196.         for ( int i = 1; i < src->cols-1; i++ )  
  197.         {  
  198.             initMSCRNode( nodeptr );  
  199.             nodeptr->index = ((src->rows-1)<<16)|i;  
  200.             if ( maskptr[0] )  
  201.             {  
  202.                 if ( maskptr[1] )  
  203.                 {  
  204.                     *total += edge->chi = *dxptr;  
  205.                     edge->left = nodeptr;  
  206.                     edge->right = nodeptr+1;  
  207.                     edge++;  
  208.                     Ne++;  
  209.                 }  
  210.                 if ( maskptr[-mask->step] )  
  211.                 {  
  212.                     *total += edge->chi = *dyptr;  
  213.                     edge->left = nodeptr-src->cols;  
  214.                     edge->right = nodeptr;  
  215.                     edge++;  
  216.                     Ne++;  
  217.                 }  
  218.             }  
  219.             dxptr++;  
  220.             dyptr++;  
  221.             nodeptr++;  
  222.             maskptr++;  
  223.         }  
  224.         initMSCRNode( nodeptr );  
  225.         nodeptr->index = ((src->rows-1)<<16)|(src->cols-1);  
  226.         if ( maskptr[0] && maskptr[-mask->step] )  
  227.         {  
  228.             *total += edge->chi = *dyptr;  
  229.             edge->left = nodeptr-src->cols;  
  230.             edge->right = nodeptr;  
  231.             Ne++;  
  232.         }  
  233.     } else {  
  234.         //定义节点指针  
  235.         MSCRNode* nodeptr = node;  
  236.         //下面是计算图像的左上角第一个像素节点  
  237.         initMSCRNode( nodeptr );    //初始化节点  
  238.         //index为对应的序列值,也就是图像的坐标,纵坐标保存在高16位内,横坐标保存在低16位内  
  239.         nodeptr->index = 0;   
  240.         //为边缘列表的卡方距离赋值,并累加该距离值  
  241.         *total += edge->chi = *dxptr;  
  242.         dxptr++;    //递增  
  243.         edge->left = nodeptr;    //边缘列表的左侧指向当前像素节点  
  244.         edge->right = nodeptr+1;    //右侧指向下一个像素节点  
  245.         edge++;    //递增  
  246.         nodeptr++;    //递增  
  247.         //下面的for循环是计算图像的第一行像素,对应的边缘列表的卡方距离保存的是水平梯度  
  248.         for ( int i = 1; i < src->cols-1; i++ )  
  249.         {  
  250.             initMSCRNode( nodeptr );  
  251.             nodeptr->index = i;      
  252.             *total += edge->chi = *dxptr;  
  253.             dxptr++;  
  254.             edge->left = nodeptr;  
  255.             edge->right = nodeptr+1;  
  256.             edge++;  
  257.             nodeptr++;  
  258.         }  
  259.         initMSCRNode( nodeptr );  
  260.         nodeptr->index = src->cols-1;    //图像第一行最后一个像素  
  261.         nodeptr++;    //指向图像的第二行  
  262.         //下面的双重for循环计算的是除了第一行和最后一行以外的像素  
  263.         for ( int i = 1; i < src->rows-1; i++ )  
  264.         {  
  265.             initMSCRNode( nodeptr );  
  266.             nodeptr->index = i<<16;    //图像的第一列  
  267.             *total += edge->chi = *dyptr;    //垂直梯度  
  268.             dyptr++;  
  269.             edge->left = nodeptr-src->cols;    //左侧为上面一行像素节点  
  270.             edge->right = nodeptr;    //右侧为当前像素节点  
  271.             edge++;  
  272.             *total += edge->chi = *dxptr;    //水平梯度  
  273.             dxptr++;  
  274.             edge->left = nodeptr;  
  275.             edge->right = nodeptr+1;  
  276.             edge++;  
  277.             nodeptr++;  
  278.             for ( int j = 1; j < src->cols-1; j++ )  
  279.             {  
  280.                 initMSCRNode( nodeptr );  
  281.                 nodeptr->index = (i<<16)|j;  
  282.                 *total += edge->chi = *dyptr;  
  283.                 dyptr++;  
  284.                 edge->left = nodeptr-src->cols;  
  285.                 edge->right = nodeptr;  
  286.                 edge++;  
  287.                 *total += edge->chi = *dxptr;  
  288.                 dxptr++;  
  289.                 edge->left = nodeptr;  
  290.                 edge->right = nodeptr+1;  
  291.                 edge++;  
  292.                 nodeptr++;  
  293.             }  
  294.             //图像最后一列像素  
  295.             initMSCRNode( nodeptr );  
  296.             nodeptr->index = (i<<16)|(src->cols-1);  
  297.             *total += edge->chi = *dyptr;  
  298.             dyptr++;  
  299.             edge->left = nodeptr-src->cols;  
  300.             edge->right = nodeptr;  
  301.             edge++;  
  302.             nodeptr++;  
  303.         }  
  304.         //图像的最后一行像素  
  305.         initMSCRNode( nodeptr );  
  306.         nodeptr->index = (src->rows-1)<<16;  
  307.         *total += edge->chi = *dxptr;  
  308.         dxptr++;  
  309.         edge->left = nodeptr;  
  310.         edge->right = nodeptr+1;  
  311.         edge++;  
  312.         *total += edge->chi = *dyptr;  
  313.         dyptr++;  
  314.         edge->left = nodeptr-src->cols;  
  315.         edge->right = nodeptr;  
  316.         edge++;  
  317.         nodeptr++;  
  318.         for ( int i = 1; i < src->cols-1; i++ )  
  319.         {  
  320.             initMSCRNode( nodeptr );  
  321.             nodeptr->index = ((src->rows-1)<<16)|i;  
  322.             *total += edge->chi = *dxptr;  
  323.             dxptr++;  
  324.             edge->left = nodeptr;  
  325.             edge->right = nodeptr+1;  
  326.             edge++;  
  327.             *total += edge->chi = *dyptr;  
  328.             dyptr++;  
  329.             edge->left = nodeptr-src->cols;  
  330.             edge->right = nodeptr;  
  331.             edge++;  
  332.             nodeptr++;  
  333.         }  
  334.         initMSCRNode( nodeptr );  
  335.         nodeptr->index = ((src->rows-1)<<16)|(src->cols-1);  
  336.         *total += edge->chi = *dyptr;  
  337.         edge->left = nodeptr-src->cols;  
  338.         edge->right = nodeptr;  
  339.     }  
  340.     return Ne;  
  341. }  

// the preprocess to get the edge list with proper gaussian blur
static int preprocessMSER_8UC3( MSCRNode* node,    //图像像素节点
            MSCREdge* edge,    //边缘列表
            double* total,    //求相似度均值时使用,这里是所有像素相似度之和
            CvMat* src,    //原始图像
            CvMat* mask,    //掩码矩阵
            CvMat* dx,    //水平梯度矩阵
            CvMat* dy,    //垂直梯度矩阵
            int Ne,    //边缘列表元素的个数
            int edgeBlurSize )    //平滑处理的孔径尺寸大小
{
    int srccpt = src->step-src->cols*3;
    uchar* srcptr = src->data.ptr;    //图像当前像素指针
    uchar* lastptr = src->data.ptr+3;    //右侧像素指针
    double* dxptr = dx->data.db;    //水平梯度数据指针
    //计算当前像素与其右侧像素之间的颜色相似度
    for ( int i = 0; i < src->rows; i++ )
    {
        //图像最右侧一列没有该相似度,因此j < src->cols-1
        for ( int j = 0; j < src->cols-1; j++ )
        {
            //公式1,计算卡方距离,保存到dx内
            *dxptr = ChiSquaredDistance( srcptr, lastptr );
            //地址递增
            dxptr++;
            srcptr += 3;
            lastptr += 3;
        }
        //指向下一行
        srcptr += srccpt+3;
        lastptr += srccpt+3;
    }
    srcptr = src->data.ptr;    //图像当前像素指针
    lastptr = src->data.ptr+src->step;    //下一行像素指针
    double* dyptr = dy->data.db;    //垂直梯度数据指针
    //计算当前像素与其下面一行像素之间的颜色相似度
    //图像最下面一行没有该相似度,因此i < src->rows-1
    for ( int i = 0; i < src->rows-1; i++ )
    {
        for ( int j = 0; j < src->cols; j++ )
        {
            //保存到dy内
            *dyptr = ChiSquaredDistance( srcptr, lastptr );
            dyptr++;
            srcptr += 3;
            lastptr += 3;
        }
        srcptr += srccpt;
        lastptr += srccpt;
    }
    // get dx and dy and blur it
    //对颜色相似度值进行高斯平滑处理
    if ( edgeBlurSize >= 1 )
    {
        cvSmooth( dx, dx, CV_GAUSSIAN, edgeBlurSize, edgeBlurSize );
        cvSmooth( dy, dy, CV_GAUSSIAN, edgeBlurSize, edgeBlurSize );
    }
    dxptr = dx->data.db;
    dyptr = dy->data.db;
    // assian dx, dy to proper edge list and initialize mscr node
    // the nasty code here intended to avoid extra loops
    /*下面的if语句是为边缘列表赋值,如果定义了掩码矩阵,则边缘列表不保存被掩码掉的像素的边缘信息,因此边缘列表的个数Ne需要重新计算并输出。在这里我们以没有定义掩码矩阵为例进行讲解,两者的本质是一样的*/
    if ( mask )
    {
        Ne = 0;
        int maskcpt = mask->step-mask->cols+1;
        uchar* maskptr = mask->data.ptr;
        MSCRNode* nodeptr = node;
        initMSCRNode( nodeptr );
        nodeptr->index = 0;
        *total += edge->chi = *dxptr;
        if ( maskptr[0] && maskptr[1] )
        {
            edge->left = nodeptr;
            edge->right = nodeptr+1;
            edge++;
            Ne++;
        }
        dxptr++;
        nodeptr++;
        maskptr++;
        for ( int i = 1; i < src->cols-1; i++ )
        {
            initMSCRNode( nodeptr );
            nodeptr->index = i;
            if ( maskptr[0] && maskptr[1] )
            {
                *total += edge->chi = *dxptr;
                edge->left = nodeptr;
                edge->right = nodeptr+1;
                edge++;
                Ne++;
            }
            dxptr++;
            nodeptr++;
            maskptr++;
        }
        initMSCRNode( nodeptr );
        nodeptr->index = src->cols-1;
        nodeptr++;
        maskptr += maskcpt;
        for ( int i = 1; i < src->rows-1; i++ )
        {
            initMSCRNode( nodeptr );
            nodeptr->index = i<<16;
            if ( maskptr[0] )
            {
                if ( maskptr[-mask->step] )
                {
                    *total += edge->chi = *dyptr;
                    edge->left = nodeptr-src->cols;
                    edge->right = nodeptr;
                    edge++;
                    Ne++;
                }
                if ( maskptr[1] )
                {
                    *total += edge->chi = *dxptr;
                    edge->left = nodeptr;
                    edge->right = nodeptr+1;
                    edge++;
                    Ne++;
                }
            }
            dyptr++;
            dxptr++;
            nodeptr++;
            maskptr++;
            for ( int j = 1; j < src->cols-1; j++ )
            {
                initMSCRNode( nodeptr );
                nodeptr->index = (i<<16)|j;
                if ( maskptr[0] )
                {
                    if ( maskptr[-mask->step] )
                    {
                        *total += edge->chi = *dyptr;
                        edge->left = nodeptr-src->cols;
                        edge->right = nodeptr;
                        edge++;
                        Ne++;
                    }
                    if ( maskptr[1] )
                    {
                        *total += edge->chi = *dxptr;
                        edge->left = nodeptr;
                        edge->right = nodeptr+1;
                        edge++;
                        Ne++;
                    }
                }
                dyptr++;
                dxptr++;
                nodeptr++;
                maskptr++;
            }
            initMSCRNode( nodeptr );
            nodeptr->index = (i<<16)|(src->cols-1);
            if ( maskptr[0] && maskptr[-mask->step] )
            {
                *total += edge->chi = *dyptr;
                edge->left = nodeptr-src->cols;
                edge->right = nodeptr;
                edge++;
                Ne++;
            }
            dyptr++;
            nodeptr++;
            maskptr += maskcpt;
        }
        initMSCRNode( nodeptr );
        nodeptr->index = (src->rows-1)<<16;
        if ( maskptr[0] )
        {
            if ( maskptr[1] )
            {
                *total += edge->chi = *dxptr;
                edge->left = nodeptr;
                edge->right = nodeptr+1;
                edge++;
                Ne++;
            }
            if ( maskptr[-mask->step] )
            {
                *total += edge->chi = *dyptr;
                edge->left = nodeptr-src->cols;
                edge->right = nodeptr;
                edge++;
                Ne++;
            }
        }
        dxptr++;
        dyptr++;
        nodeptr++;
        maskptr++;
        for ( int i = 1; i < src->cols-1; i++ )
        {
            initMSCRNode( nodeptr );
            nodeptr->index = ((src->rows-1)<<16)|i;
            if ( maskptr[0] )
            {
                if ( maskptr[1] )
                {
                    *total += edge->chi = *dxptr;
                    edge->left = nodeptr;
                    edge->right = nodeptr+1;
                    edge++;
                    Ne++;
                }
                if ( maskptr[-mask->step] )
                {
                    *total += edge->chi = *dyptr;
                    edge->left = nodeptr-src->cols;
                    edge->right = nodeptr;
                    edge++;
                    Ne++;
                }
            }
            dxptr++;
            dyptr++;
            nodeptr++;
            maskptr++;
        }
        initMSCRNode( nodeptr );
        nodeptr->index = ((src->rows-1)<<16)|(src->cols-1);
        if ( maskptr[0] && maskptr[-mask->step] )
        {
            *total += edge->chi = *dyptr;
            edge->left = nodeptr-src->cols;
            edge->right = nodeptr;
            Ne++;
        }
    } else {
        //定义节点指针
        MSCRNode* nodeptr = node;
        //下面是计算图像的左上角第一个像素节点
        initMSCRNode( nodeptr );    //初始化节点
        //index为对应的序列值,也就是图像的坐标,纵坐标保存在高16位内,横坐标保存在低16位内
        nodeptr->index = 0; 
        //为边缘列表的卡方距离赋值,并累加该距离值
        *total += edge->chi = *dxptr;
        dxptr++;    //递增
        edge->left = nodeptr;    //边缘列表的左侧指向当前像素节点
        edge->right = nodeptr+1;    //右侧指向下一个像素节点
        edge++;    //递增
        nodeptr++;    //递增
        //下面的for循环是计算图像的第一行像素,对应的边缘列表的卡方距离保存的是水平梯度
        for ( int i = 1; i < src->cols-1; i++ )
        {
            initMSCRNode( nodeptr );
            nodeptr->index = i;    
            *total += edge->chi = *dxptr;
            dxptr++;
            edge->left = nodeptr;
            edge->right = nodeptr+1;
            edge++;
            nodeptr++;
        }
        initMSCRNode( nodeptr );
        nodeptr->index = src->cols-1;    //图像第一行最后一个像素
        nodeptr++;    //指向图像的第二行
        //下面的双重for循环计算的是除了第一行和最后一行以外的像素
        for ( int i = 1; i < src->rows-1; i++ )
        {
            initMSCRNode( nodeptr );
            nodeptr->index = i<<16;    //图像的第一列
            *total += edge->chi = *dyptr;    //垂直梯度
            dyptr++;
            edge->left = nodeptr-src->cols;    //左侧为上面一行像素节点
            edge->right = nodeptr;    //右侧为当前像素节点
            edge++;
            *total += edge->chi = *dxptr;    //水平梯度
            dxptr++;
            edge->left = nodeptr;
            edge->right = nodeptr+1;
            edge++;
            nodeptr++;
            for ( int j = 1; j < src->cols-1; j++ )
            {
                initMSCRNode( nodeptr );
                nodeptr->index = (i<<16)|j;
                *total += edge->chi = *dyptr;
                dyptr++;
                edge->left = nodeptr-src->cols;
                edge->right = nodeptr;
                edge++;
                *total += edge->chi = *dxptr;
                dxptr++;
                edge->left = nodeptr;
                edge->right = nodeptr+1;
                edge++;
                nodeptr++;
            }
            //图像最后一列像素
            initMSCRNode( nodeptr );
            nodeptr->index = (i<<16)|(src->cols-1);
            *total += edge->chi = *dyptr;
            dyptr++;
            edge->left = nodeptr-src->cols;
            edge->right = nodeptr;
            edge++;
            nodeptr++;
        }
        //图像的最后一行像素
        initMSCRNode( nodeptr );
        nodeptr->index = (src->rows-1)<<16;
        *total += edge->chi = *dxptr;
        dxptr++;
        edge->left = nodeptr;
        edge->right = nodeptr+1;
        edge++;
        *total += edge->chi = *dyptr;
        dyptr++;
        edge->left = nodeptr-src->cols;
        edge->right = nodeptr;
        edge++;
        nodeptr++;
        for ( int i = 1; i < src->cols-1; i++ )
        {
            initMSCRNode( nodeptr );
            nodeptr->index = ((src->rows-1)<<16)|i;
            *total += edge->chi = *dxptr;
            dxptr++;
            edge->left = nodeptr;
            edge->right = nodeptr+1;
            edge++;
            *total += edge->chi = *dyptr;
            dyptr++;
            edge->left = nodeptr-src->cols;
            edge->right = nodeptr;
            edge++;
            nodeptr++;
        }
        initMSCRNode( nodeptr );
        nodeptr->index = ((src->rows-1)<<16)|(src->cols-1);
        *total += edge->chi = *dyptr;
        edge->left = nodeptr-src->cols;
        edge->right = nodeptr;
    }
    return Ne;
}

 

 

 

下面我们再总结一下preprocessMSER_8UC3函数,首先根据公式1计算卡方距离,当前像素与其右侧像素之间的距离放在dx中,当前像素与其下面像素之间的距离放在dy中,存放的顺序都是从图像的左上角至图像的右下角。另外图像的最右一列没有dx,图像的最下一行没有dy。然后对dx和dy进行高斯平滑处理。最后创建边缘列表。边缘列表的顺序也是从图像的左上角至图像的右下角,与dx和dy的顺序完全一致,并且个数是dx与dy数量之和。如果边缘列表元素的卡方距离(edge->chi)为dx,则它的左侧(edge->left)和右侧(edge->right)分别指向的是图像的当前像素节点和它的右侧像素节点,因此对于图像的最右侧像素节点,没有dx,只有dy;如果边缘列表元素的卡方距离(edge->chi)为dy,则它的左侧(edge->left)和右侧(edge->right)分别指向的是图像的当前像素上一行像素节点和当前像素节点,因此对于图像的第一行没有dy,只有dx;图像的右上角的一个像素既没有dx,也没有dy;而其余像素节点既有dx,又有dy。

 

下面给出应用MSCR的应用实例:

 

[cpp] view plain copy print?在CODE上查看代码片派生到我的代码片

  1. #include "opencv2/core/core.hpp"  
  2. #include "opencv2/highgui/highgui.hpp"  
  3. #include "opencv2/imgproc/imgproc.hpp"  
  4. #include <opencv2/features2d/features2d.hpp>  
  5. #include <iostream>  
  6. using namespace cv;  
  7. using namespace std;  
  8.   
  9. int main(int argc, char *argv[])  
  10. {  
  11.     Mat src,yuv;  
  12.     src = imread("puzzle.png");  
  13.     cvtColor(src, yuv, COLOR_BGR2YCrCb);  
  14.   
  15.     MSER ms;  
  16.     vector<vector<Point>> regions;  
  17.     ms(yuv, regions, Mat());  
  18.     for (int i = 0; i < regions.size(); i++)  
  19.     {  
  20.         ellipse(src, fitEllipse(regions[i]), Scalar(255,0,0));  
  21.     }  
  22.     imshow("mscr", src);  
  23.     waitKey(0);  
  24.     return 0;  
  25. }  

#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <opencv2/features2d/features2d.hpp>
#include <iostream>
using namespace cv;
using namespace std;

int main(int argc, char *argv[])
{
    Mat src,yuv;
	src = imread("puzzle.png");
	cvtColor(src, yuv, COLOR_BGR2YCrCb);

    MSER ms;
    vector<vector<Point>> regions;
    ms(yuv, regions, Mat());
    for (int i = 0; i < regions.size(); i++)
    {
        ellipse(src, fitEllipse(regions[i]), Scalar(255,0,0));
    }
    imshow("mscr", src);
    waitKey(0);
    return 0;
}

 

 

 

 

 

从程序中可以看出,在进行MSCR之前,需要把RGB彩色图像转换为YCrCb形式,如果直接应用RGB彩色空间,则会检测到一些不正确的区域。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/App_12062011/article/details/51952907

智能推荐

Antd Table组件表头不对齐解决方案_antd table 表头错位-程序员宅基地

文章浏览阅读9.8k次,点赞3次,收藏7次。Antd Table组件表头不对齐解决方案背景:在使用Antd的table组件时,由于表头参数过多,于是设置了scroll属性,在其超出一定宽度后进行滚动但是在添加了该属性之后,经常会出现表头不对齐的问题:针对该问题Google 了一下解决方案,但大多不是很完善,为解决问题。现整理下完整的解决方案:1、对表格的每一行 【columns】设置width属性(留出一行进行宽度自适应);..._antd table 表头错位

javascript笔记:推荐使用“百度统计”并且拷贝百度统计的前端框架-程序员宅基地

文章浏览阅读100次。  本篇博文讲两件事情,一个是推荐在博客园经常写博客的童鞋们一个很棒的工具--“百度统计”,另一个是“拷贝百度统计”的页面框架。  首先讲第一个事情,我的博客里有不少文章都是讲“用户行为分析”的,虽然现在不做这个方向的项目,但是对它的兴趣不减,所以我今天在自己博客里部署了百度的用户行为分析系统“百度统计”的采集脚本,感受的确是很棒,使用了百度统计后我发现我更能和看过我博客的童鞋们进行互动了。..._nav-more__hover

eclipse CXF +Spring 开发 WebService_eclipse10.5开发cxf与spring整合调用webservice-程序员宅基地

文章浏览阅读1.7k次。Java WebService运行环境eclipseJDK 1.8Tomcat 7.0cxf 3.2.1CXF下载http://download.csdn.net/download/yioow/10147610(cxf 3.2.1) http://cxf.apache.org/download.html(官方)1.新建Dynamic Web..._eclipse10.5开发cxf与spring整合调用webservice

东北大学研究生:用强化学习玩Chrome里的恐龙小游戏-程序员宅基地

文章浏览阅读3.9k次,点赞6次,收藏23次。&#13; &#13;&#13; &#13;&#13; &#13; &#13; 【人工智能头条导读】Chrome 浏览器里面有一个小彩蛋,当你没有网络时,打开任意的 URL 都..._恐龙游戏强化学习

树莓派安装OpenCV_树莓派numpy1.19.5对应的opencv-程序员宅基地

文章浏览阅读1.9k次。ImportError: numpy.core.multiarray failed to importmportError: libcblas.so.3: cannot open shared object file: No such file or directory_树莓派numpy1.19.5对应的opencv

Tomcat安装以及配置-程序员宅基地

文章浏览阅读296次。一:查看自己电脑操作系统位数在’我的电脑’点击右键打开属性,查找自己电脑的操作系统属性二:下载Tomcat官网:Tomcat下载点击自己需要的版本选择自己电脑操作系统对应的版本,Windows64位建议下载我中的第三个下载解压后得到对应的文件(笔者修改了文件名,大家可以根据需要修改)三:配置环境变量首先点击电脑属性,找到高级系统设置然后找到环境变量,并进入点击新建在变量名中填写:CATALINA_HOME(不要留空格),然后点击浏览目录,找到自己Tomcat的解压目录,

随便推点

Python3 压缩与解压缩(zlib / gzip / bz2 / lzma / zipfile / tarfile)_zlib解压 python3-程序员宅基地

文章浏览阅读6.8k次,点赞2次,收藏13次。Python3 压缩与解压缩(zlib / gzip / bz2 / lzma / zipfile / tarfile)文件的归档 (各种格式的压缩 / 解压缩)实际使用中仅需要使用shutil模块的压缩和解压函数就可以了, 如果想尝试其他功能, zipfile(暴力破解), tarfile(命令行)也是值得推荐的_zlib解压 python3

Tomcat环境变量配置_# tomcat environment-程序员宅基地

文章浏览阅读215次。计算机——属性——高级系统设置——环境变量——系统变量添加CATALINA_HOME变量,值就是Tomcat的根目录_# tomcat environment

增量和非增量的集成测试_非增量集成测试的关键模板是什么-程序员宅基地

文章浏览阅读299次。_非增量集成测试的关键模板是什么

HTML--BOM window对象(实例:放大镜)_html 里面 bom案例-程序员宅基地

文章浏览阅读3.9k次。目录简介:1、BOM结构2、window对象3、location对象4、history对象5、navigator对象6、screen对象BOM 定时器1、定时器方法放大镜简介:BOM即浏览器对象模型(Browser Object Model),它提供了页面与浏览器窗口进行交互的对象接口。BOM由一系列的相关对象组成,window作为BOM的顶层对象,所有其他全局对象都是window的子对象,甚至DOM也是其子对象之一。学会了window对象及其子对象的常用_html 里面 bom案例

python中popen的用法_python中的subprocess.Popen()使用-程序员宅基地

文章浏览阅读564次。从python2.4版本开始,可以用subprocess这个模块来产生子进程,并连接到子进程的标准输入/输出/错误中去,还可以得到子进程的返回值。subprocess意在替代其他几个老的模块或者函数,比如:os.system os.spawn* os.popen* popen2.* commands.*一、subprocess.Popensubprocess模块定义了一个类: Popenclass..._python中popen的用法

Bootstrap 做网页需要布局,轮播图,缩略图,表格,表单等元素。_spicefood主页布局-程序员宅基地

文章浏览阅读1.1k次。<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>响应._spicefood主页布局