博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
分水岭算法(理论+opencv实现)
阅读量:4287 次
发布时间:2019-05-27

本文共 6929 字,大约阅读时间需要 23 分钟。

分水岭算法理论

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


原始的分水岭:

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

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

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

分类图

模拟结果图

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

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

       这是我们想要的吗?

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

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

模拟分类图 

模拟结果图


 opencv改进的分水岭算法:

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

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

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

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

 模拟分类图

模拟结果图


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

opencv实现:

1 #include 
2 #include
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
> contours;35 vector
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
(i+1)是为了分水岭的标记不同,区域1、2、3。。。。这样才能分割41 drawContours(showImage, contours, static_cast
(i), Scalar::all(static_cast
(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
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
(row, col);62 if (index > 0 && index <= contours.size()) {63 dst.at
(row, col) = colors[index - 1];64 }65 else if (index == -1)66 {67 dst.at
(row, col) = Vec3b(255, 255, 255);68 }69 else {70 dst.at
(row, col) = Vec3b(0, 0, 0);71 }72 }73 }74 }

 

 分水岭合并代码:

1 void segMerge(Mat& image, Mat& segments, int& numSeg) 2 { 3     vector
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
(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
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 else51 {52 hist_bases.push_back(Mat());53 }54 }55 double similarity = 0;56 vector
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;//被合并的区域标志位true69 if (i != j)//这里没必要,i不可能等于j70 {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
(p, k);77 if (index == j) segments.at
(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图像处理编程实例》

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

你可能感兴趣的文章
hibernate抓取策略fetch
查看>>
Hibernate的N+1条SQL查询问题-------Iterate
查看>>
hibernate 缓存机制
查看>>
悲观锁
查看>>
Spring官网下载的步骤
查看>>
Spring入门之-------搭建步骤
查看>>
Spring源码和jar包下载步骤
查看>>
Spring依赖注入(dependency injection)
查看>>
一位资深程序员大牛给予Java初学者的学习路线建议
查看>>
spring中bean的自动装配和作用域
查看>>
java中关于“==”和“equals()”方法的区别
查看>>
Could not create the view: An unexpected exception was thrown.
查看>>
mybatis入门之Helloworld
查看>>
使用http请求,中文乱码问题--解决方法
查看>>
mybatis入门之接口式编程
查看>>
html页面中iframe嵌套页面的父页面和子页面js方法互相调用
查看>>
求字符串中变换位置问题
查看>>
求这个字符串中的最大的数字字符串
查看>>
Spring报错:java.lang.ClassCastException: com.sun.proxy.$Proxy2 cannot be cast to *
查看>>
Java一键启动Linux上的tomcat服务器
查看>>