opencv图像处理学习(四十一)——分水岭分割

1.特征

分水岭分割是基于自然的启发算法来模拟水流通过地形起伏的现象从而研究总结出来的一种分割方法,其基本原理是将图像特征看做地理上的地貌特征,利用像素的灰度值分布特征,对每个符合特征的区域进行划分,形成边界以构成分水岭。分水岭分割的图像被认为是地形起伏,其中所述梯度大小被解释为高度相关信息。对于分水岭算法中的图像像素点,一般需要关注下面三种特征点:

(1)局部最值点,该类点反映出来的特征是图像中的局部最小值或局部最大值点,水流会会聚到最小点,水流在最大值点不能驻留,分水岭中极值特性是分析图像分割中的重要技术之一。

(2)交汇边缘点,该类点的产生就是由于灰度不均匀变化引起的,对应于分水岭中不同地域形成的交接点,有着同样的概率来接收分界点流失的水域。

(3)连接区域点,通过局部最小值的像素点向外慢慢扩散,集水区间内的像素点会承担连接作用,同时水域流向会通过连接区域点流到局部最小值点。

分水岭算法是一种很好的分割相互接触的物体图像的方式,但在边缘分割时精确度却存在一定的问题。在实际应用场景中,存在的噪声或其他干扰信息会导致局部极值点,这样就无法实现图像分割。

分水岭算法是一种很好的分割相互接触的物体图像的方式,但在边缘分割时精确度却存在一定的问题。在实际应用场景中,存在的噪声或其他干扰信息会导致局部极值点过多,这样就无法实现图像分割。分水岭算法改进了漫水控制标记相关技术,自动分割图像中显著表现出的流域区域。

具有不同颜色的区域在这里被分割成图像中不同的部分,在这幅图像中我们可以看到前景与背景被分割,正如我们看到的相关的图像物体形状一样。对图像中某些小区域的背景与前景分割将意味着会有更少的交汇边缘点,这样就容易造成过分隔现象,常用的解决方法是合并视觉上的小区域部分,将其视为独立部分进行分割操作。

2.实现

在计算机视觉中我们常常关注的目标特征是颜色和灰度,刻画图像中的两个区域视觉相似性有许多方法,如形状描述子、颜色特征、距特征等。对于某种场景下的应用,具有独特的纹理对象可以使用一个很好的纹理描述符。针对颜色不同的区域中的单个对象相同的扩展,我们可以使用颜色特征来测量对象的不同部分的相似性。如果物体各个子区域存在单一的颜色分布,我们可以使用颜色特征来测量物体的不同区域的相似性概率。

分水岭分割算法的实现途径有多种,具体思路为:首先,对源图像进行灰度化,并使用OTSU进行二值化操作;其次,对二值化图像进行形态学开操作,进行距离变换操作,最后,归一化距离变换的统计图像,并计算相应连通域分割块。

Mat watershedSegment(Mat &srcImage,int &noOfSegments)
{
    Mat grayMat;
    Mat otsuMat;
    cvtcolor(srcImage,grayMat,CV_BGR2GRAY);
    threshold(grayMat,otsuMat,0,255,CV_THRESH_BINARY_INV+CV_THRESH_OTSU);
    morphology(otsuMat,otsuMat,MORPH_OPEN,Mat::ones(9,9,CV_8SC1),Point(4,4),2);
    Mat disTranMat(otsuMat.rows,otsuMat.cols,CV_32FC1);
    distanceTransform(otsuMat,disTranMat,CV_DIST_L2,3);
    normalize(disTranMat,disTranMat,0.0,1,NORM_MINMAX);
    threshold(disTranMat,disTranMat,0.1,1,CV_THRESH_BINARY);
    normalize(disTranMat,disTranMat,0.0,255.0,NORM_MINMAX);
    disTranMat.convertTo(disTranMat,CV_8UC1)

    //计算标记的分割块
    int i,j,compCount = 0;
    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;
    findContours(disTranMat,contours,hierarchy,CV_RETR_CCOMP,CV_CHAIN_APPROX_SIMPLE);
    if(contours.empty())
    {
        return Mat();
    }
    Mat markers(disTranMat.size(),CV_32S);
    markers = Scalar::all(0);
    int idx = 0;
    //绘制区域块
    for(;idx>=0;idx=hierarchy[idx][0],compCount++)
    {
          drawContours(markers,contours,idx,Scalar::all(compCount+1),-1,8,hierarchy,INT_MAX);
        
    }
    if(compCount == 0)
    {
        return Mat();
    }
    double t = (double)getTickCount();
    watershed(srcImage,markers);
    t = (double)getTickCount() - t;
    printf("execution time = %gms\n",t*1000./getTickFrequency());
    Mat wshed = displaySegResult(markers,compCount);
    noOfSegment = compCount;
    return markers; 
}
cv::Mat displaySegResult(cv::Mat & segments, int numOfSegments, cv::Mat & image) 
{ 
    cv::Mat wshed(segments.size(), CV_8UC3); // 创建对于颜色分量 
    vector<Vec3b> colorTab;
     
    for (int i = 0; i < numOfSegments; i++) 
    {
         int b = theRNG().uniform(0, 255); 
         int g = theRNG().uniform(0, 255); 
         int r = theRNG().uniform(0, 255);
         colorTab.push_back(Vec3b((uchar)b, (uchar)g,(uchar)r)); 
    } //应用不同颜色对每个部分 
    
    for (int i = 0; i < segments.rows; i++) 
    {
         for (int j = 0; j < segments.cols; j++)
         { 
            int index = segments.at<int>(i, j); 
            if (index == -1) 
            wshed.at<Vec3b>(i, j) = Vec3b(255, 255, 255); 
            else if (index <= 0 || index > numOfSegments)
            wshed.at<Vec3b>(i, j) = Vec3b(0, 0, 0); 
            else 
            wshed.at<Vec3b>(i, j) = colorTab[index - 1]; 
         }
     }
     
     if (image.dims>0)
     wshed = wshed*0.5 + image*0.5; 
     return wshed; 
}

3.分割合并

由色度图可以看出,颜色在分水岭分割的不同区域扩展在相同的目标物体内是相似的,可以使用色度饱和度直方图提取图像的每个区域,并测量了其巴特查亚距离用于其距离相似性测量。巴特查理亚距离测量的是两个分离或连续区域部分概率分布的相似性,当测量距离很小时,两个区域将被分水岭合并成单个部分,当图像的过分隔现象明显时,这种合并可重复进行。分水岭分割合并首先计算分割部分的像素归属,利用直方图信息统计相关特征,对每个分割部分统计直方图对比的相似性,然后根据相似性判断分割的两个部分是否需要合并成一个区域。

void segMerge(Mat &image,Mat &segments,int &numSeg)
{
    vector<Mat> samples;//对一个分割部分进行像素统计
    int newNumSeg = numSrg;//统计数据更新
    //初始化分割部分
    for(int i=0;i<=numSeg;i++)
    {
        Mat sampleImage;
        samples.push_back(sampleImage);
    }
    //统计每一个部分
    for(int i =0;i<segment.rows;i++)
    {
        for(int j=0;j<segments.cols;j++)
        {
            //检查每个像素的归属
            int index = segments.at<int>(i,j);
            if(index >= 0 && index < numSeg)
            {
                samples[index].push_back(image(Rect(j,i,1,1)));
            }
        }
    }
    //创建直方图
    vector<MatND> hist_bases;
    Mat hsv_base;
    //设置直方图参数
    int h_bins = 35;
    int s_bins = 30;
    int histSize[] = {h_bins,s_bins};
    //hue变换范围0~256,saturation变换范围0~180
    float h_ranges[] = {0,256};
    const float* ranges[] = {h_ranges,s_ranges};
    //使用第0与第1通道
    int channels[] = {0.1};
    //生成直方图
    MatND hist_base;
    for(int c = 1;c<numSeg;c++)
    {
        if(sample[c].dims>0)
        {
            //将区域部分转换成HSV
            cvtColor(sample[c],hsv_base,CV_BGR2HSV);
            //直方图统计
            calcHist(&hsv_base,1,channels,Mat(),hist_base,histSize,ranges,true,false);
            //直方图统一化
            normalize(hist_base,hist_base,0,1,NORM_MINMAX,-1,Mat());
            //添加到统计集
            hist_base.push_back(hist_base);
        }
        else
        {
            hist_base.push_back(MatND());
        }
        hist_base.relese();
    }

    double similarity = 0;
    vector<bool> mearged;
    for(int k = 0;k<hist_bases.size();k++)
    {
        mearged.push_back(false);
    }
    //统计每一个部分的直方图相似
    for(int c = 0;c<hist_bases.size();c++)
    {
        for(int q=c+1;q<hist_bases.size();q++)
        {
            if(!mearged[q])
            {
                //判断直方图维度
                if(hist_bases[c].dims>0 && hist_bases[q].dims>0)
                {
                  //直方图对比
                    similarity = compareHist(hist_bases[c],hist_bases[q],CV_COMP+BHATTACHARYYA);
                    if(similarity > 0.8)
                    {
                        mearged[q] = true;
                        if(q != c)   
                        {
                            newNumSeg--;//减少区域部分
                            for(int i=0;i<Segments.rows;i++)
                            {
                                for(int j=0;j<Segments.cols;j++)
                                {
                                    int index=segments.at<int>(i.j);
                                    //合并
                                    int(index=q)
                                    {
                                        segments.at<int>(i,j)=c;
                                    }
                                }
                            }
                        }
                    }  
                }
            }
        }
    }
    numSeg = newNumSeg;
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值