使用OpenCV和C++实现的分水岭算法(Watershed)
2013-05-29 11:03
721 查看
分水岭算法(watershed)是一种比较基本的数学形态学分割算法,其基本思想是将灰度图像转换为梯度图像,将梯度值看作高低起伏的山岭,将局部极小值及其邻域看作一个“集水盆”。设想一个个“集水盆”中存在积水,且水位不断升高,淹没梯度较低的地方,当水漫过程停止后,图像就可以被分割成几块连通区域。
分水岭算法有不同的实现方法。本文要实现的是通过人为标注一些种子点,将这些种子点看作集水盆的底部,利用区域增长的方法,完成图像的分割。试图实现OpenCV中cv::watershed函数的功能,经过测试,与OpenCV相比分割结果相似,但性能差很多。(前者32ms左右,后者8ms左右,原因可能是循环中使用了cv::mat来访问图像中的元素,改用指针速度可能会提高很多)。
OpenCV函数的运行结果:(OpenCV函数对分割边缘也做了处理,我写的那个程序没有)
程序运行结果:
参考:
http://wenku.baidu.com/view/d1fde240336c1eb91a375d95.html
/article/7805577.html
分水岭算法有不同的实现方法。本文要实现的是通过人为标注一些种子点,将这些种子点看作集水盆的底部,利用区域增长的方法,完成图像的分割。试图实现OpenCV中cv::watershed函数的功能,经过测试,与OpenCV相比分割结果相似,但性能差很多。(前者32ms左右,后者8ms左右,原因可能是循环中使用了cv::mat来访问图像中的元素,改用指针速度可能会提高很多)。
OpenCV函数的运行结果:(OpenCV函数对分割边缘也做了处理,我写的那个程序没有)
程序运行结果:
参考:
http://wenku.baidu.com/view/d1fde240336c1eb91a375d95.html
/article/7805577.html
#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)
- Android(安卓)开发通过NDK调用JNI,使用opencv做本地c++代码开发配置方法实现边缘检测代码(2)
- OpenCV2 使用分水岭算法对图像分割的个人理解 cv::watershed()
- C++使用opencv实现彩色直方图计算
- 使用OpenCV实现分水岭算法
- 使用c++简单实现reactor模式
- 【OpenCV】使用floodfill()实现PhotoShop魔棒功能
- [c/c++]使用宏函数实现的无锁队列
- 【C++ STL应用与实现】18: 怎样使用迭代器适配器
- 使用C++ 11 实现阻塞队列
- 在C#中使用C++的DLL,并且在DLL中包含有opencv的代码,通过“障眼法”操作
- Opencv3中画图功能详解(C++实现,python说明)
- C++ 0x 使用可变参数模板类 实现 C# 的委托机制
- gabor 滤波的c++实现与该类得使用简介
- 使用c++实现一个FTP客户端(一)
- 【C++ STL应用与实现】56: 使用std::unique删除重复元素
- OpenCV3使用meanshift实现目标跟踪
- 如何使用opencv的c++接口来读取、写结构体数组到yml文件中
- 使用opencv的SVM和神经网络实现车牌识别
- 使用C++实现虚幻4通电游戏