现在的位置: 首页 > 综合 > 正文

Opencv2.4.9源码分析——MSCR

2018年04月22日 ⁄ 综合 ⁄ 共 13362字 ⁄ 字号 评论关闭


前面我们介绍了MSER方法,但该方法不适用于对彩色图像的区域检测。为此,Forssen2007年提出了针对彩色图像的最大稳定极值区域的检测方法——MSCRMaximally
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* >
m
min
       
4

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

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

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

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

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

opencv2.4.9中,MSCRMSER共用一个类:

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;
};

MSCRMSER多用了几个参数:


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


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


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


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

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

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函数:

// 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。然后对dxdy进行高斯平滑处理。最后创建边缘列表。边缘列表的顺序也是从图像的左上角至图像的右下角,与dxdy的顺序完全一致,并且个数是dxdy数量之和。如果边缘列表元素的卡方距离(edge->chi)为dx,则它的左侧(edge->left)和右侧(edge->right)分别指向的是图像的当前像素节点和它的右侧像素节点,因此对于图像的最右侧像素节点,没有dx,只有dy;如果边缘列表元素的卡方距离(edge->chi)为dy,则它的左侧(edge->left)和右侧(edge->right)分别指向的是图像的当前像素上一行像素节点和当前像素节点,因此对于图像的第一行没有dy,只有dx;图像的右上角的一个像素既没有dx,也没有dy;而其余像素节点既有dx,又有dy


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

#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彩色空间,则会检测到一些不正确的区域。

抱歉!评论已关闭.