• 分水岭算法(理论+opencv实现)


    分水岭算法理论

     

      从意思上就知道通过用水来进行分类,学术上说什么基于拓扑结构的形态学。。。其实就是根据把图像比作一副地貌,然后通过最低点和最高点去分类!


    原始的分水岭:

     

      就是上面说的方式,接下来用一幅图进行解释---->>>

          把图像用一维坐标表示,二维和三维不好画,必须用matlab了,我不会用,意思可以表述到位

     

    •       第一步:找到图像的局部最低点,这个方法很多了,可以用一个内核去找,也可以一个一个比较,实现起来不难。
    •       第二步:从最低点开始注水,水开始网上满(图像的说法就是梯度法),其中那些最低点已经被标记,不会被淹没,那些中间点是被淹没的。
    •       第三步:找到局部最高点,就是图中3位置对应的两个点。
    •       第四步:这样基于局部最小值,和找到的局部最大值,就可以分割图像了。

     

     

    分类图

    模拟结果图

     

      是不是感觉上面的方法很好,也很简单?接着看下面的图:

           利用上面的步骤,第一步找到了三个点,然后第二步开始漫水,这三个点都被记录下来了,又找到两个局部最大值。

           这是我们想要的吗?

           回答是否定的!其中中间那个最小值我们不需要,因为只是一个很少并且很小的噪点而已,我们不需要图像分割的那么细致。

         缺陷显露出来了吧?没关系,下面我们的opencv把这个问题解决了。

    模拟分类图 

    模拟结果图


     opencv改进的分水岭算法:

       

      针对上面出现的问题,我们想到的是能不能给这种小细节一个标记,让它不属于我们找的最小的点呢?

       opencv对其改进就是使用了人工标记的方法,我们标记一些点,基于这些点去引导分水岭算法的进行,效果很好! 

           比如我们对上面的图像标记了两个三角形,第一步我们找到三个局部最小点,第二步淹没的时候三个点都被淹没了,然而中间那个没被标记,那就淹死了(没有救生圈),其余两个点保留,这样就可以达到我们的想要的结果了。

      注释:这里的标记是用不同的标号进行的,我为了方便使用了同样的三角形了。因为标记用来分类,所以不同的标记打上不同的标号!这在下面opencv程序中体现了。。。

     模拟分类图

    模拟结果图


    注释:具体的实现没有完成,感觉原理懂了会使用了这样就可以了,当你需要深入的时候再去研究实现的算法,当你浅浅的使用懂了原理应该会改一点,面试过了完全可以啊!哈哈哈~~

    opencv实现:

     1 #include <opencv2/opencv.hpp>
     2 #include <iostream>
     3 
     4 using namespace cv;
     5 using namespace std;
     6 
     7 void waterSegment(InputArray& _src, OutputArray& _dst, int& noOfSegment);
     8 
     9 int main(int argc, char** argv) {
    10     
    11     Mat inputImage = imread("coins.jpg");
    12     assert(!inputImage.data);
    13     Mat graImage, outputImage;
    14     int offSegment;
    15     waterSegment(inputImage, outputImage, offSegment);
    16 
    17     waitKey(0);
    18     return 0;
    19 }
    20 
    21 void waterSegment(InputArray& _src,OutputArray& _dst,int& noOfSegment)
    22 {
    23     Mat src = _src.getMat();//dst = _dst.getMat();
    24     Mat grayImage;
    25     cvtColor(src, grayImage,CV_BGR2GRAY);
    26     threshold(grayImage, grayImage, 0, 255, THRESH_BINARY | THRESH_OTSU);
    27     Mat kernel = getStructuringElement(MORPH_RECT, Size(9, 9), Point(-1, -1));
    28     morphologyEx(grayImage, grayImage, MORPH_CLOSE, kernel);
    29     distanceTransform(grayImage, grayImage, DIST_L2, DIST_MASK_3, 5);
    30     normalize(grayImage, grayImage,0,1, NORM_MINMAX);
    31     grayImage.convertTo(grayImage, CV_8UC1);
    32     threshold(grayImage, grayImage,0,255, THRESH_BINARY | THRESH_OTSU);
    33     morphologyEx(grayImage, grayImage, MORPH_CLOSE, kernel);
    34     vector<vector<Point>> contours;
    35     vector<Vec4i> hierarchy;
    36     Mat showImage = Mat::zeros(grayImage.size(), CV_32SC1);
    37     findContours(grayImage, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(-1, -1));
    38     for (size_t i = 0; i < contours.size(); i++)
    39     {
    40         //这里static_cast<int>(i+1)是为了分水岭的标记不同,区域1、2、3。。。。这样才能分割
    41         drawContours(showImage, contours, static_cast<int>(i), Scalar::all(static_cast<int>(i+1)), 2);
    42     }
    43     Mat k = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
    44     morphologyEx(src, src, MORPH_ERODE, k);
    45     watershed(src, showImage);
    46 
    47     //随机分配颜色
    48     vector<Vec3b> colors;
    49     for (size_t i = 0; i < contours.size(); i++) {
    50         int r = theRNG().uniform(0, 255);
    51         int g = theRNG().uniform(0, 255);
    52         int b = theRNG().uniform(0, 255);
    53         colors.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));
    54     }
    55 
    56     // 显示
    57     Mat dst = Mat::zeros(showImage.size(), CV_8UC3);
    58     int index = 0;
    59     for (int row = 0; row < showImage.rows; row++) {
    60         for (int col = 0; col < showImage.cols; col++) {
    61             index = showImage.at<int>(row, col);
    62             if (index > 0 && index <= contours.size()) {
    63                 dst.at<Vec3b>(row, col) = colors[index - 1];
    64             }
    65             else if (index == -1)
    66             {
    67                 dst.at<Vec3b>(row, col) = Vec3b(255, 255, 255);
    68             }
    69             else {
    70                 dst.at<Vec3b>(row, col) = Vec3b(0, 0, 0);
    71             }
    72         }
    73     }
    74 }

     

     分水岭合并代码:

     1 void segMerge(Mat& image, Mat& segments, int& numSeg)
     2 {
     3     vector<Mat> samples;
     4     int newNumSeg = numSeg;
     5     //初始化变量长度的Vector
     6     for (size_t i = 0; i < newNumSeg; i++)
     7     {
     8         Mat sample;
     9         samples.push_back(sample);
    10     }
    11     for (size_t i = 0; i < segments.rows; i++)
    12     {
    13         for (size_t j = 0; j < segments.cols; j++)
    14         {
    15             int index = segments.at<uchar>(i, j);
    16             if (index >= 0 && index <= newNumSeg)//把同一个区域的点合并到一个Mat中
    17             {
    18                 if (!samples[index].data)//数据为空不能合并,否则报错
    19                 {
    20                     samples[index] = image(Rect(j, i, 1, 1));
    21                 }
    22                 else//按行合并
    23                 {
    24                     vconcat(samples[index], image(Rect(j, i, 2, 1)), samples[index]);
    25                 }
    26             }
    27             //if (index >= 0 && index <= newNumSeg)
    28             //    samples[index].push_back(image(Rect(j, i, 1, 1)));
    29         }
    30     }
    31     vector<Mat> hist_bases;
    32     Mat hsv_base;
    33     int h_bins = 35;
    34     int s_bins = 30;
    35     int histSize[2] = { h_bins , s_bins };
    36     float h_range[2] = { 0,256 };
    37     float s_range[2] = { 0,180 };
    38     const float* range[2] = { h_range,s_range };
    39     int channels[2] = { 0,1 };
    40     Mat hist_base;
    41     for (size_t i = 1; i < numSeg; i++)
    42     {
    43         if (samples[i].dims > 0)
    44         {
    45             cvtColor(samples[i], hsv_base, CV_BGR2HSV);
    46             calcHist(&hsv_base, 1, channels, Mat(), hist_base, 2, histSize, range);
    47             normalize(hist_base, hist_base, 0, 1, NORM_MINMAX);
    48             hist_bases.push_back(hist_base);
    49         }
    50         else
    51         {
    52             hist_bases.push_back(Mat());
    53         }
    54     }
    55     double similarity = 0;
    56     vector<bool> merged;//是否合并的标志位
    57     for (size_t i = 0; i < hist_bases.size(); i++)
    58     {
    59         for (size_t j = i+1; j < hist_bases.size(); j++)
    60         {
    61             if (!merged[j])//未合并的区域进行相似性判断
    62             {
    63                 if (hist_bases[i].dims > 0 && hist_bases[j].dims > 0)//这里维数判断没必要,直接用个data就可以了
    64                 {
    65                     similarity = compareHist(hist_bases[i], hist_bases[j], HISTCMP_BHATTACHARYYA);
    66                     if (similarity > 0.8)
    67                     {
    68                         merged[j] = true;//被合并的区域标志位true
    69                         if (i != j)//这里没必要,i不可能等于j
    70                         {
    71                             newNumSeg --;//分割部分减少
    72                             for (size_t p = 0; p < segments.rows; p++)
    73                             {
    74                                 for (size_t k = 0; k < segments.cols; k++)
    75                                 {
    76                                     int index = segments.at<uchar>(p, k);
    77                                     if (index == j) segments.at<uchar>(p, k) = i;
    78                                 }
    79                             }
    80                         }
    81                     }
    82                 }
    83             }
    84         }
    85     }
    86     numSeg = newNumSeg;//返回合并之后的区域数量
    87 }

    参考:

        http://blog.csdn.net/iracer/article/details/49225823

        http://www.cnblogs.com/mikewolf2002/p/3304118.html

        http://lib.csdn.net/article/opencv/22776

        《opencv图像处理编程实例》

        代码参考贾老师视频,原理早就看了毛星云的书本,但是当时一知半解,现在从头看一下子就懂了。

  • 相关阅读:
    PHP实现html字符实体转汉字
    利用 secureCRT 直接上传下载文件 (sz,rz)
    CentOS安装scp命令
    以Apache模块的方式编译安装php-5.5.4
    编译安装 apache 2.4.6
    协方差Covariance的表述推导
    Java _ JDK _ Arrays, LinkedList, ArrayList, Vector 及Stack
    Java_一些特殊的关键字详(?)解
    Java_你应该知道的26种设计模式
    排序与搜索一览
  • 原文地址:https://www.cnblogs.com/wjy-lulu/p/7056466.html
Copyright © 2020-2023  润新知