分水岭算法理论

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


原始的分水岭:

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

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

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

分类图

模拟结果图

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

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

       这是我们想要的吗?

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

     缺陷显露出来了吧?没关系,下面我们的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();
return ;
} 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, , , THRESH_BINARY | THRESH_OTSU);
Mat kernel = getStructuringElement(MORPH_RECT, Size(, ), Point(-, -));
morphologyEx(grayImage, grayImage, MORPH_CLOSE, kernel);
distanceTransform(grayImage, grayImage, DIST_L2, DIST_MASK_3, );
normalize(grayImage, grayImage,,, NORM_MINMAX);
grayImage.convertTo(grayImage, CV_8UC1);
threshold(grayImage, grayImage,,, 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(-, -));
for (size_t i = ; 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+)), );
}
Mat k = getStructuringElement(MORPH_RECT, Size(, ), Point(-, -));
morphologyEx(src, src, MORPH_ERODE, k);
watershed(src, showImage); //随机分配颜色
vector<Vec3b> colors;
for (size_t i = ; i < contours.size(); i++) {
int r = theRNG().uniform(, );
int g = theRNG().uniform(, );
int b = theRNG().uniform(, );
colors.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));
} // 显示
Mat dst = Mat::zeros(showImage.size(), CV_8UC3);
int index = ;
for (int row = ; row < showImage.rows; row++) {
for (int col = ; col < showImage.cols; col++) {
index = showImage.at<int>(row, col);
if (index > && index <= contours.size()) {
dst.at<Vec3b>(row, col) = colors[index - ];
}
else if (index == -)
{
dst.at<Vec3b>(row, col) = Vec3b(, , );
}
else {
dst.at<Vec3b>(row, col) = Vec3b(, , );
}
}
}
}

分水岭合并代码:

 void segMerge(Mat& image, Mat& segments, int& numSeg)
{
vector<Mat> samples;
int newNumSeg = numSeg;
//初始化变量长度的Vector
for (size_t i = ; i < newNumSeg; i++)
{
Mat sample;
samples.push_back(sample);
}
for (size_t i = ; i < segments.rows; i++)
{
for (size_t j = ; j < segments.cols; j++)
{
int index = segments.at<uchar>(i, j);
if (index >= && index <= newNumSeg)//把同一个区域的点合并到一个Mat中
{
if (!samples[index].data)//数据为空不能合并,否则报错
{
samples[index] = image(Rect(j, i, , ));
}
else//按行合并
{
vconcat(samples[index], image(Rect(j, i, , )), 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 = ;
int s_bins = ;
int histSize[] = { h_bins , s_bins };
float h_range[] = { , };
float s_range[] = { , };
const float* range[] = { h_range,s_range };
int channels[] = { , };
Mat hist_base;
for (size_t i = ; i < numSeg; i++)
{
if (samples[i].dims > )
{
cvtColor(samples[i], hsv_base, CV_BGR2HSV);
calcHist(&hsv_base, , channels, Mat(), hist_base, , histSize, range);
normalize(hist_base, hist_base, , , NORM_MINMAX);
hist_bases.push_back(hist_base);
}
else
{
hist_bases.push_back(Mat());
}
}
double similarity = ;
vector<bool> merged;//是否合并的标志位
for (size_t i = ; i < hist_bases.size(); i++)
{
for (size_t j = i+; j < hist_bases.size(); j++)
{
if (!merged[j])//未合并的区域进行相似性判断
{
if (hist_bases[i].dims > && hist_bases[j].dims > )//这里维数判断没必要,直接用个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 = ; p < segments.rows; p++)
{
for (size_t k = ; 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图像处理编程实例》

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

分水岭算法(理论+opencv实现)的更多相关文章

  1. 第八节、图片分割之GrabCut算法、分水岭算法

    所谓图像分割指的是根据灰度.颜色.纹理和形状等特征把图像划分成若干互不交迭的区域,并使这些特征在同一区域内呈现出相似性,而在不同区域间呈现出明显的差异性.我们先对目前主要的图像分割方法做个概述,后面再 ...

  2. 图片分割之GrabCut算法、分水岭算法

    https://www.cnblogs.com/zyly/p/9392881.html 所谓图像分割指的是根据灰度.颜色.纹理和形状等特征把图像划分成若干互不交迭的区域,并使这些特征在同一区域内呈现出 ...

  3. OpenCV——分水岭算法

    分水岭算法,是一种基于拓扑理论的数学形态学的分割方法,其基本思想是把图像看作是测地学上的拓扑地貌,图像中每一点像素的灰度值表示该点的海拔高度,每一个局部极小值及其影响区域称为集水盆,而集水盆的边界则形 ...

  4. opencv分水岭算法对图像进行切割

    先看效果 说明 使用分水岭算法对图像进行切割,设置一个标记图像能达到比較好的效果,还能防止过度切割. 1.这里首先对阈值化的二值图像进行腐蚀,去掉小的白色区域,得到图像的前景区域.并对前景区域用255 ...

  5. opencv学习之路(30)、分水岭算法及图像修补

    一.简介 二.分水岭算法 #include "opencv2/opencv.hpp" using namespace cv; void main() { Mat srcImg = ...

  6. OpenCV 学习笔记 04 深度估计与分割——GrabCut算法与分水岭算法

    1 使用普通摄像头进行深度估计 1.1 深度估计原理 这里会用到几何学中的极几何(Epipolar Geometry),它属于立体视觉(stereo vision)几何学,立体视觉是计算机视觉的一个分 ...

  7. OpenCV学习(9) 分水岭算法(3)

    本教程我学习一下opencv中分水岭算法的具体实现方式. 原始图像和Mark图像,它们的大小都是32*32,分水岭算法的结果是得到两个连通域的轮廓图. 原始图像:(原始图像必须是3通道图像) Mark ...

  8. OpenCV学习(8) 分水岭算法(2)

        现在我们看看OpenCV中如何使用分水岭算法.     首先我们打开一副图像:    // 打开另一幅图像   cv::Mat    image= cv::imread("../to ...

  9. Opencv分水岭算法——watershed自动图像分割用法

    分水岭算法是一种图像区域分割法,在分割的过程中,它会把跟临近像素间的相似性作为重要的参考依据,从而将在空间位置上相近并且灰度值相近的像素点互相连接起来构成一个封闭的轮廓,封闭性是分水岭算法的一个重要特 ...

随机推荐

  1. OK335xS U-boot 编译问题&无Linux shell 问题

    /************************************************************************** * OK335xS U-boot 编译问题&am ...

  2. 小程序引入多个e-charts

    小程序引入e-charts图表 这里是狗尾草第一次发表掘金文章,日后望各位大佬多多支持~ 前言:运营助手,见名知意,没有图表数据的展示,看上去是有多空白.因此,俺们UI做了很好的交互,一个页面来了4个 ...

  3. nmap扫描时的2个小经验

    http://pnig0s1992.blog.51cto.com/393390/367558/ 1.我肉鸡的环境是Windows XP sp3,在使用nmap扫描外网的时候,提示我 pcap_open ...

  4. 新建Android一个项目-菜鸟篇

    ①打开Eclipse,单击菜单栏的“File”->把鼠标光标移动到“New”->在弹出的列表框中,如果直接能看到“Android Applicaion Project”选项项,则直接单击此 ...

  5. 结构体内的函数与bfs的情景变量

    关于结构体内的函数,太难的尚且不会用,下面是一个简单一点的结构体内函数的代码 定义这样一个结构体函数之后就能像如下这样使用了 以上为结构体内的简单函数,下面重点来了,关于bfs找最短路由于需要避免走回 ...

  6. 使用Visual Studio Code开发Asp.Net Core WebApi学习笔记(二)-- Web Api Demo

    在上一篇里,我已经建立了一个简单的Web-Demo应用程序.这一篇将记录将此Demo程序改造成一个Web Api应用程序. 一.添加ASP.NET Core MVC包 1. 在project.json ...

  7. StreamSets 管理 SDC Edge上的pipeline

    可选的方式: ui (data colelctor) 发送命令 UI 主要是创建edge pipeline 的时候进行edge server 的配置 默认是 http://localhost:1863 ...

  8. nyoj 最小公倍数

    最小公倍数 时间限制:1000 ms  |  内存限制:65535 KB 难度:3   描述 为什么1小时有60分钟,而不是100分钟呢?这是历史上的习惯导致. 但也并非纯粹的偶然:60是个优秀的数字 ...

  9. [CLPR] 卷积还是相关? - Opencv之filter2D探究

    I am doing something about convolving images in Python and for sake of speed I chose opencv 2.4.9. O ...

  10. 无人驾驶之激光雷达&摄像头(主要from 速腾CEO 邱纯鑫分享)

    无人驾驶之激光雷达&摄像头 (from 速腾CEO 邱纯鑫公开课分享) 根据听的一些讲座和看的书籍,个人感觉:目前现在的自动驾驶,根本问题还是在于感知(路况,周边物体,交通标识等等),控制的方 ...