分水岭算法(watershed)是一种比较基本的数学形态学分割算法,其基本思想是将灰度图像转换为梯度图像,将梯度值看作高低起伏的山岭,将局部极小值及其邻域看作一个“集水盆”。设想一个个“集水盆”中存在积水,且水位不断升高,淹没梯度较低的地方,当水漫过程停止后,图像就可以被分割成几块连通区域。

分水岭算法有不同的实现方法。本文要实现的是通过人为标注一些种子点,将这些种子点看作集水盆的底部,利用区域增长的方法,完成图像的分割。试图实现OpenCV中cv::watershed函数的功能,经过测试,与OpenCV相比分割结果相似,但性能差很多。(前者32ms左右,后者8ms左右,原因可能是循环中使用了cv::mat来访问图像中的元素,改用指针速度可能会提高很多)。

OpenCV函数的运行结果:(OpenCV函数对分割边缘也做了处理,我写的那个程序没有)

程序运行结果:

参考:

http://wenku.baidu.com/view/d1fde240336c1eb91a375d95.html

http://blog.csdn.net/fdl19881/article/details/6749976

#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/objdetect/objdetect.hpp>
#include <opencv2/highgui/highgui.hpp>
#include<vector>
#include<iostream>
#include<queue>
#include<fstream>
cv::Mat marker_mask;
cv::Mat g_markers;
cv::Mat img0, img, img_gray,wshed;
cv::Point_<int> prev_pt(-1,-1);
using std::vector;
using std::queue;
static void my_watershed(cv::Mat img,cv::Mat& markers,int comp_count);
static void mouse_event(int event,int x, int y,int flags, void*)
{if(img.rows==0)return;if(event==CV_EVENT_LBUTTONUP||!(flags&CV_EVENT_FLAG_LBUTTON))prev_pt=cv::Point_<int>(-1,-1);else if(event==CV_EVENT_LBUTTONDOWN)prev_pt=cv::Point2i(x,y);else if(event==CV_EVENT_MOUSEMOVE&&(flags&CV_EVENT_FLAG_LBUTTON)){cv::Point2i pt(x,y);if(prev_pt.x<0)prev_pt=pt;cv::line(marker_mask,prev_pt,pt,cv::Scalar(255,255,255),1,8,0);cv::line(img,prev_pt,pt,cv::Scalar(255,255,255),1,8,0);prev_pt=pt;cv::imshow("image",img);}
}
int main()
{img0=cv::imread("Lenna.png",1);img=img0.clone();CvRNG rng = cvRNG(-1); img_gray=img0.clone();wshed=img0.clone();marker_mask=cv::Mat(cv::Size(img0.cols,img0.rows),8,1);g_markers=cv::Mat(cv::Size(img0.cols,img0.rows),CV_32S,1);cv::cvtColor(img,marker_mask,CV_BGR2GRAY);cv::cvtColor(marker_mask,img_gray,CV_GRAY2BGR);for(int i=0;i<marker_mask.rows;i++)for(int j=0;j<marker_mask.cols;j++)marker_mask.at<unsigned char>(i,j)=0;for(int i=0;i<g_markers.rows;i++)for(int j=0;j<g_markers.cols;j++)g_markers.at<int>(i,j)=0;cv::imshow("image",img);cv::imshow("watershed transform",wshed);cv::setMouseCallback("image",mouse_event,0);for(;;){int c=cv::waitKey(0);if((char)c==27)break;if((char)c=='r'){for(int i=0;i<marker_mask.rows;i++)for(int j=0;j<marker_mask.cols;j++)marker_mask.at<unsigned char>(i,j)=0;img0.copyTo(img);cv::imshow("image",img);}if((char)c=='w'||(char)c==' '){vector<vector<cv::Point>> contours;CvMat* color_tab=0;int comp_count=0;cv::findContours(marker_mask,contours,CV_RETR_CCOMP,CV_CHAIN_APPROX_SIMPLE,cv::Point(0,0));for(int i=0;i<g_markers.rows;i++)for(int j=0;j<g_markers.cols;j++)g_markers.at<int>(i,j)=0;vector<vector<cv::Point> >::iterator iter=contours.begin();for(int i=0;i<(int)contours.size();i++){cv::drawContours(g_markers,contours,i,cv::Scalar::all(comp_count+1),1,8,vector<cv::Vec4i>());comp_count++;}if(comp_count==0)continue;color_tab=cvCreateMat(1,comp_count,CV_8UC3);for(int i=0;i<comp_count;i++){uchar* ptr=color_tab->data.ptr+i*3;ptr[0]=(uchar)(cvRandInt(&rng)%180+50);ptr[1]=(uchar)(cvRandInt(&rng)%180+50);ptr[2]=(uchar)(cvRandInt(&rng)%180+50);}cv::Mat temp=g_markers.clone();double t=(double)cvGetTickCount();//my_watershed(img0,g_markers,comp_count);cv::watershed(img0,g_markers);t=(double)cvGetTickCount()-t;std::cout<<"exec time= "<<t/(cvGetTickFrequency()*1000.)<<std::endl;for(int i=0;i<g_markers.rows;i++)for(int j=0;j<g_markers.cols;j++){int idx=g_markers.at<int>(i,j);uchar* dst=&wshed.at<uchar>(i,j*3);if(idx==-1)dst[0]=dst[1]=dst[2]=(uchar)255;else if(idx<=0||idx>comp_count)dst[0]=dst[1]=dst[2]=(uchar)8;else{uchar* ptr=color_tab->data.ptr+(idx-1)*3;dst[0]=ptr[0];dst[1]=ptr[1];dst[2]=ptr[2];}}cv::addWeighted(wshed,0.5,img_gray,0.5,0,wshed);cv::imshow("watershed transform",wshed);cvReleaseMat(&color_tab);}}return 0;
}
static void my_watershed(cv::Mat img0,cv::Mat& markers,int comp_count)
{cv::Mat gray=cv::Mat(cv::Size(img0.rows,img0.cols),8,1);cv::cvtColor(img0,gray,CV_BGR2GRAY);cv::Mat imge=cv::Mat(cv::Size(img0.rows,img0.cols),8,1);cv::Sobel(gray,imge,CV_8U,1,1);vector<queue<cv::Point2i>*>Labeleddata;//图像中各连通区域的点queue<cv::Point2i>* pque;//某连通区域已包含的点queue<cv::Point2i> quetem; //用于提取某连通区域中输入种子点中的初始种子点vector<int*> SeedCounts;int* Array;cv:: Point2i temp;int row=imge.rows,col=imge.cols;cv::Mat marker_saved=markers.clone();bool up,down,right,left,uplef,uprig,downlef,downrig;int m,n;for(int i=0;i<comp_count;i++){Array=new int[256];SeedCounts.push_back(Array);//统计某waterlevel的各个连通区域中种子点的个数pque=new queue<cv::Point2i>[256]; Labeleddata.push_back(pque);//存储该连通区域中种子生长所得的点        }for(int i=0;i<row;i++)for(int j=0;j<col;j++){if(markers.at<int>(i,j)>0){temp.x=i;temp.y=j;quetem.push(temp);int num=markers.at<int>(i,j);markers.at<int>(i,j)=-1;//该点已处理,其他种子点生长时将绕过该点while(!quetem.empty()){up=down=right=left=uplef=uprig=downlef=downrig=false;temp=quetem.front(); //提取出一个点,在该点的八连通区域内寻找可生长点m=temp.x;n=temp.y;quetem.pop();if(m-1>=0)//若上方可生长则添加为新种子{if(markers.at<int>(m-1,n)==num){temp.x=m-1;temp.y=n;quetem.push(temp);markers.at<int>(m-1,n)=-1;}else{up=true;}}if(m-1>=0&&n-1>=0){if(markers.at<int>(m-1,n-1)==num){temp.x=m-1;temp.y=n-1;quetem.push(temp);markers.at<int>(m-1,n-1)=-1;}else{uplef=true;}}if(m+1<=row-1){if(markers.at<int>(m+1,n)==num){temp.x=m+1;temp.y=n;quetem.push(temp);markers.at<int>(m+1,n)=-1;}else{down=true;}}if(m+1<=row-1&&n+1<=col-1){if(markers.at<int>(m+1,n+1)==num){temp.x=m+1;temp.y=n+1;quetem.push(temp);markers.at<int>(m+1,n+1)=-1;}else{downrig=true;}}if(n+1<=col-1){if(markers.at<int>(m,n+1)==num){temp.x=m;temp.y=n+1;quetem.push(temp);markers.at<int>(m,n+1)=-1;}else{right=true;}}if(m-1>=0&&n+1<=col-1){if(markers.at<int>(m-1,n+1)==num){temp.x=m-1;temp.y=n+1;quetem.push(temp);markers.at<int>(m-1,n+1)=-1;}else{uprig=true;}}if(n-1>=0){if(markers.at<int>(m,n-1)==num){temp.x=m;temp.y=n-1;quetem.push(temp);markers.at<int>(m,n-1)=-1;}else{left=true;}}if(m+1<=row-1&&n-1>=0){if(markers.at<int>(m+1,n-1)==num){temp.x=m+1;temp.y=n-1;quetem.push(temp);markers.at<int>(m+1,n-1)=-1;}else{downlef=true;}}//八连通区域中有未标记点,则该点属于初始种子点if(up||down||right||left||uplef||downlef||uprig||downrig){temp.x=m;temp.y=n;Labeleddata[comp_count-1][imge.at<uchar>(m,n)].push(temp);SeedCounts[comp_count-1][imge.at<uchar>(m,n)]++;}}}}bool active;int waterlevel;for(waterlevel=0;waterlevel<180;waterlevel++){active=true;while(active) //当1-count_com个连通区域都无可生长点时结束循环{active=false;for(int i=0;i<comp_count;i++)//将区域i中将waterlevel梯度以下的点用于区域增长{if(!Labeleddata[i][waterlevel].empty())//区域增长,经过多次迭代,直至该区域,该waterlevel无可生长点。{active=true;  while(SeedCounts[i][waterlevel]>0) //SeedCount中保留了前一轮生长后各区域,各waterlevel的种子点个数,本轮生长结束后,将根据Labeleddata中的元素个数更新{SeedCounts[i][waterlevel]--;temp=Labeleddata[i][waterlevel].front();Labeleddata[i][waterlevel].pop();m=temp.x;n=temp.y;int num=marker_saved.at<int>(m,n);if(m-1>=0){if(!marker_saved.at<int>(m-1,n))//上方点未处理过{temp.x=m-1;temp.y=n;marker_saved.at<int>(m-1,n)=num;if(imge.at<uchar>(m-1,n)<=waterlevel)Labeleddata[i][waterlevel].push(temp);else{Labeleddata[i][imge.at<uchar>(m-1,n)].push(temp); //本次生长不处理,可能在waterlevel变化到某值时再用于生长SeedCounts[i][imge.at<uchar>(m-1,n)]++;}}}if(m+1<=row-1){if(!marker_saved.at<int>(m+1,n)){temp.x=m+1;temp.y=n;marker_saved.at<int>(m+1,n)=num;if(imge.at<uchar>(m+1,n)<=waterlevel)Labeleddata[i][waterlevel].push(temp);else{Labeleddata[i][imge.at<uchar>(m+1,n)].push(temp);SeedCounts[i][imge.at<uchar>(m+1,n)]++;}}}if(n+1<=col-1){if(!marker_saved.at<int>(m,n+1)){temp.x=m;temp.y=n+1;marker_saved.at<int>(m,n+1)=num;if(imge.at<uchar>(m,n+1)<=waterlevel)Labeleddata[i][waterlevel].push(temp);else{Labeleddata[i][imge.at<uchar>(m,n+1)].push(temp);SeedCounts[i][imge.at<uchar>(m,n+1)]++;}}}if(n-1>=0){if(!marker_saved.at<int>(m,n-1)){temp.x=m;temp.y=n-1;marker_saved.at<int>(m,n-1)=num;if(imge.at<uchar>(m,n-1)<=waterlevel)Labeleddata[i][waterlevel].push(temp);else{Labeleddata[i][imge.at<uchar>(m,n-1)].push(temp);SeedCounts[i][imge.at<uchar>(m,n-1)]++;}}}}SeedCounts[i][waterlevel]=Labeleddata[i][waterlevel].size();}}}}markers=marker_saved.clone();
}

使用OpenCV和C++实现的分水岭算法(Watershed)相关推荐

  1. OpenCV学习(二十) :分水岭算法:watershed()

    OpenCV学习(二十) :分水岭算法:watershed() 参考博客: OpenCV-分水岭算法 图像处理--分水岭算法 OpenCV学习(7) 分水岭算法(1) Opencv分水岭算法--wat ...

  2. OpenCV与图像处理学习十一——分水岭算法(含代码)

    OpenCV与图像处理学习十一--分水岭算法(含代码) 一.分水岭算法概要 二.分水岭算法步骤 三.代码应用 一.分水岭算法概要 任意的灰度图像可以被看做是地质学表面,高亮度的地方是山峰,低亮度的地方 ...

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

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

  4. OpenCV3学习(7.2)——图像分割之二(分水岭算法watershed)

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

  5. OpenCV4.5.5学习笔记(十七):分水岭算法watershed(),图像修补inpaint()

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 一.分水岭算法watershed() 二.图像修补inpaint() 总结 前言 笔者本科时候有幸接触了OpenCV3 ...

  6. OpenCV 源码中分水岭算法 watershed 函数源码注解

    为了研究分水岭算法,阅读了OpenCV 2.4.9 中watershed函数的源码实现部分,代码位于 opencv\sources\modules\imgproc\src\segmentation.c ...

  7. Opencv 分水岭算法 watershed的图像分割

    分水岭算法 参考博客: (1)迈克老狼2012   https://www.cnblogs.com/mikewolf2002/p/3304118.html (2)-牧野-              h ...

  8. 毛星云Opencv之8.5.2分水岭算法

    #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/highgui/highgui.hpp" #i ...

  9. Python OpenCV学习笔记之:分水岭算法分割图像

    为什么80%的码农都做不了架构师?>>>    # -*- coding: utf-8 -*- """ 图像分水岭分割图像 分水岭算法可以参考:http ...

最新文章

  1. UPS电池延时估算方法及配置表,ups配置不再难
  2. Java开发:(执行系统(例cmd)命令)Runtime.getRuntime().exec()参数解释
  3. javascript第一天
  4. 化学版2048,你玩过吗?内含游戏链接
  5. 真香警告!2021Android高级面试题,挥泪整理面经
  6. 凸多边形面积_C++计算任意多边形的面积
  7. while循环在Python中的应用举例 及其break continue的使用
  8. Python IDLE 快捷键
  9. html div 转图片或视频投放大屏
  10. transformer:self-attention 自注意力机制详解
  11. 转载:世上最全的百物妙用窍门-绝对不能错过,不断更新中
  12. 通信原理实践(五)——2PSK 与2DPSK 通信系统
  13. 15. PHP 全局变量 - 超全局变量
  14. SQL 复合查询条件(AND,OR,NOT)对NULL值的处理
  15. python查找相似图片
  16. python爬取ajax_Python爬虫如-何爬取ajax网页之爬取雪球网文章
  17. 深度学习入门,Keras Conv2D类参数详解
  18. 【零基础深度学习教程第一课:深度学习基础】
  19. 列表(list)使用方法详解
  20. 第一周-机器学习监督学习-无监督学习

热门文章

  1. clip python_python中numpy模块下的np.clip()的用法
  2. bad transparency mask. 解决办法
  3. 使用Matlab对频繁2项集和频繁3项集的求取
  4. 前端项目中上传图片如何实现的
  5. 除了Java,还可以培训学习哪些IT技术?
  6. linux系统c语言重命名文件,C语言文件操作函数
  7. 如何获取汉字对应的拼音
  8. 记一次阿里云ECS被挂挖矿代码的处理历程
  9. 荣耀手表es鸿蒙,荣耀手表ES评测:方形大屏+轻盈机身,599元腕上健康全能管家...
  10. java future 异常处理_Java异常处理总结