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

Posted 人工智能爱好者社区

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了分水岭算法(理论+opencv实现)相关的知识,希望对你有一定的参考价值。

‍‍‍‍

个人博客:hhttp://www.cnblogs.com/wjy-lulu/

往期回顾:


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

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

原始的分水岭:

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

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

  • 第一步:找到图像的局部最低点,这个方法很多了,可以用一个内核去找,也可以一个一个比较,实现起来不难。

  • 第二步:从最低点开始注水,水开始网上满(图像的说法就是梯度法),其中那些最低点已经被标记,不会被淹没,那些中间点是被淹没的。

  • 第三步:找到局部最高点,就是图中3位置对应的两个点。

  • 第四步:这样基于局部最小值,和找到的局部最大值,就可以分割图像了。

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

 分类图

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

模拟结果图

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

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

这是我们想要的吗?

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

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

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


模拟分类图 

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

模拟结果图



 opencv改进的分水岭算法:

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

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

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

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

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

 模拟分类图

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

模拟结果图

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

opencv实现:


#include <opencv2/opencv.hpp>

#include <iostream>


using namespace cv;

using namespace std;


void waterSegment(InputArray& _src, OutputArray& _dst, int& noOfSegment);


int main(int argc, char** argv) {

    

    Mat inputImage = imread("coins.jpg");

    assert(!inputImage.data);

    Mat graImage, outputImage;

    int offSegment;

    waterSegment(inputImage, outputImage, offSegment);


    waitKey(0);

    return 0;

}


void waterSegment(InputArray& _src,OutputArray& _dst,int& noOfSegment)

{

    Mat src = _src.getMat();//dst = _dst.getMat();

    Mat grayImage;

    cvtColor(src, grayImage,CV_BGR2GRAY);

    threshold(grayImage, grayImage, 0, 255, THRESH_BINARY | THRESH_OTSU);

    Mat kernel = getStructuringElement(MORPH_RECT, Size(9, 9), Point(-1, -1));

    morphologyEx(grayImage, grayImage, MORPH_CLOSE, kernel);

    distanceTransform(grayImage, grayImage, DIST_L2, DIST_MASK_3, 5);

    normalize(grayImage, grayImage,0,1, NORM_MINMAX);

    grayImage.convertTo(grayImage, CV_8UC1);

    threshold(grayImage, grayImage,0,255, THRESH_BINARY | THRESH_OTSU);

    morphologyEx(grayImage, grayImage, MORPH_CLOSE, kernel);

    vector<vector<Point>> contours;

    vector<Vec4i> hierarchy;

    Mat showImage = Mat::zeros(grayImage.size(), CV_32SC1);

    findContours(grayImage, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(-1, -1));

    for (size_t i = 0; i < contours.size(); i++)

    {

        //这里static_cast<int>(i+1)是为了分水岭的标记不同,区域1、2、3。。。。这样才能分割

        drawContours(showImage, contours, static_cast<int>(i), Scalar::all(static_cast<int>(i+1)), 2);

    }

    Mat k = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));

    morphologyEx(src, src, MORPH_ERODE, k);

    watershed(src, showImage);


    //随机分配颜色

    vector<Vec3b> colors;

    for (size_t i = 0; i < contours.size(); i++) {

        int r = theRNG().uniform(0, 255);

        int g = theRNG().uniform(0, 255);

        int b = theRNG().uniform(0, 255);

        colors.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));

    }


    // 显示

    Mat dst = Mat::zeros(showImage.size(), CV_8UC3);

    int index = 0;

    for (int row = 0; row < showImage.rows; row++) {

        for (int col = 0; col < showImage.cols; col++) {

            index = showImage.at<int>(row, col);

            if (index > 0 && index <= contours.size()) {

                dst.at<Vec3b>(row, col) = colors[index - 1];

            }

            else if (index == -1)

            {

                dst.at<Vec3b>(row, col) = Vec3b(255, 255, 255);

            }

            else {

                dst.at<Vec3b>(row, col) = Vec3b(0, 0, 0);

            }

        }

    }

}

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

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


 分水岭合并代码:


void segMerge(Mat& image, Mat& segments, int& numSeg)

{

    vector<Mat> samples;

    int newNumSeg = numSeg;

    //初始化变量长度的Vector

    for (size_t i = 0; i < newNumSeg; i++)

    {

        Mat sample;

        samples.push_back(sample);

    }

    for (size_t i = 0; i < segments.rows; i++)

    {

        for (size_t j = 0; j < segments.cols; j++)

        {

            int index = segments.at<uchar>(i, j);

            if (index >= 0 && index <= newNumSeg)//把同一个区域的点合并到一个Mat中

            {

                if (!samples[index].data)//数据为空不能合并,否则报错

                {

                    samples[index] = image(Rect(j, i, 1, 1));

                }

                else//按行合并

                {

                    vconcat(samples[index], image(Rect(j, i, 2, 1)), samples[index]);

                }

            }

            //if (index >= 0 && index <= newNumSeg)

            //    samples[index].push_back(image(Rect(j, i, 1, 1)));

        }

    }

    vector<Mat> hist_bases;

    Mat hsv_base;

    int h_bins = 35;

    int s_bins = 30;

    int histSize[2] = { h_bins , s_bins };

    float h_range[2] = { 0,256 };

    float s_range[2] = { 0,180 };

    const float* range[2] = { h_range,s_range };

    int channels[2] = { 0,1 };

    Mat hist_base;

    for (size_t i = 1; i < numSeg; i++)

    {

        if (samples[i].dims > 0)

        {

            cvtColor(samples[i], hsv_base, CV_BGR2HSV);

            calcHist(&hsv_base, 1, channels, Mat(), hist_base, 2, histSize, range);

            normalize(hist_base, hist_base, 0, 1, NORM_MINMAX);

            hist_bases.push_back(hist_base);

        }

        else

        {

            hist_bases.push_back(Mat());

        }

    }

    double similarity = 0;

    vector<bool> merged;//是否合并的标志位

    for (size_t i = 0; i < hist_bases.size(); i++)

    {

        for (size_t j = i+1; j < hist_bases.size(); j++)

        {

            if (!merged[j])//未合并的区域进行相似性判断

            {

                if (hist_bases[i].dims > 0 && hist_bases[j].dims > 0)//这里维数判断没必要,直接用个data就可以了

                {

                    similarity = compareHist(hist_bases[i], hist_bases[j], HISTCMP_BHATTACHARYYA);

                    if (similarity > 0.8)

                    {

                        merged[j] = true;//被合并的区域标志位true

                        if (i != j)//这里没必要,i不可能等于j

                        {

                            newNumSeg --;//分割部分减少

                            for (size_t p = 0; p < segments.rows; p++)

                            {

                                for (size_t k = 0; k < segments.cols; k++)

                                {

                                    int index = segments.at<uchar>(p, k);

                                    if (index == j) segments.at<uchar>(p, k) = i;

                                }

                            }

                        }

                    }

                }

            }

        }

    }

    numSeg = newNumSeg;//返回合并之后的区域数量

}



参考:

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

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

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

    《opencv图像处理编程实例》

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




回复 人工智能          揭开人工智能的神秘面纱

回复 贝叶斯算法      贝叶斯算法与新闻分类

回复 机器学习          R&Python机器学习

回复 阿里数据          阿里数据系列课程

回复 Python            Python机器学习案例实战

回复 Spark              征服Spark第一季

回复 kaggle             机器学习kaggle案例

回复 大数据             大数据系列视频

回复 数据分析         数据分析人员的转型

回复 数据挖掘         数据挖掘与人工智能

回复 机器学习         R&Python机器学习

回复 阿里数据         阿里数据系列课程

回复 R                     R&Python机器学习入门


以上是关于分水岭算法(理论+opencv实现)的主要内容,如果未能解决你的问题,请参考以下文章

OpenCV——分水岭算法

opencv::基于距离变换与分水岭的图像分割

使用openCV分水岭算法实现图像分割

OpenCV(26)图像分割 -- 距离变换与分水岭算法(硬币检测扑克牌检测车道检测)

OpenCV进行图像分割:分水岭算法(相关函数介绍以及项目实现)

OpenCV中的图像处理 —— 霍夫线 / 圈变换 + 图像分割(分水岭算法) + 交互式前景提取(GrabCut算法)