您的位置:首页 > 运维架构

【opencv学习之三十八】图像的分水岭算法

2018-02-21 11:52 567 查看
分水岭算法主要根据图像梯度将图像分割成“山”和“谷”;一般图像噪声经常干扰分水岭算法的分割,所以一般采用标记的方法来给分水岭算法提供灰度级参考,来更换的分割图像;从效果来说比普通的灰度阈值分割效果要好;算法的具体原理和实现可参考网上的详解;
原函数及解释:
CV_EXPORTS_W void watershed( InputArray image, InputOutputArray markers );
image :输入8比特3通道图像。 
markers :输入或输出的32比特单通道标记图像。 
函数Watershed实现在[Meyer92]描述的变量分水岭,基于非参数标记的分割算法中的一种。在把图像传给函数之前,用户需要用正指标大致勾画出图像标记的感兴趣区域。比如,每一个区域都表示成一个或者多个像素值1,2,3的互联部分。这些部分将作为将来图像区域的种子。标记中所有的其他像素,他们和勾画出的区域关系不明并且应由算法定义,应当被置0。这个函数的输出则是标记区域所有像素被置为某个种子部分的值,或者在区域边界则置-1。 
下面是两个应用例子:1.rice的分割;///////////////////分水岭

Vec3b RandomColor(int value) //生成随机颜色函数
{
value=value%255; //生成0~255的随机数
RNG rng;
int aa=rng.uniform(0,value);
int bb=rng.uniform(0,value);
int cc=rng.uniform(0,value);
return Vec3b(aa,bb,cc);
}

void imgWatershed2()//分水岭1
{
Mat image= imread("D:/ImageTest/Rice.png"); //载入RGB彩色图像
imshow("Source Image",image);

//灰度化,滤波,Canny边缘检测
Mat imageGray;
cvtColor(image,imageGray,CV_RGB2GRAY);//灰度转换
GaussianBlur(imageGray,imageGray,Size(5,5),2); //高斯滤波
imshow("Gray Image",imageGray);
Canny(imageGray,imageGray,80,150);
imshow("Canny Image",imageGray);

//查找轮廓
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(imageGray,contours,hierarchy,RETR_TREE,CHAIN_APPROX_SIMPLE,Point());
Mat imageContours=Mat::zeros(image.size(),CV_8UC1); //轮廓
Mat marks(image.size(),CV_32S); //Opencv分水岭第二个矩阵参数
marks=Scalar::all(0);
int index = 0;
int compCount = 0;
for( ; index >= 0; index = hierarchy[index][0], compCount++ )
{
//对marks进行标记,对不同区域的轮廓进行编号,相当于设置注水点,有多少轮廓,就有多少注水点
drawContours(marks, contours, index, Scalar::all(compCount+1), 1, 8, hierarchy);
drawContours(imageContours,contours,index,Scalar(255),1,8,hierarchy);
}

//我们来看一下传入的矩阵marks里是什么东西
Mat marksShows;
convertScaleAbs(marks,marksShows);
imshow("marksShow",marksShows);
imshow("contours",imageContours);
watershed(image,marks);

//我们再来看一下分水岭算法之后的矩阵marks里是什么东西
Mat afterWatershed;
convertScaleAbs(marks,afterWatershed);
imshow("After Watershed",a
4000
fterWatershed);

//对每一个区域进行颜色填充
Mat PerspectiveImage=Mat::zeros(image.size(),CV_8UC3);
for(int i=0;i<marks.rows;i++)
{
for(int j=0;j<marks.cols;j++)
{
int index=marks.at<int>(i,j);
if(marks.at<int>(i,j)==-1)
{
PerspectiveImage.at<Vec3b>(i,j)=Vec3b(255,255,255);
}
else
{
PerspectiveImage.at<Vec3b>(i,j) =RandomColor(index);
}
}
}
imshow("After ColorFill",PerspectiveImage);

//分割并填充颜色的结果跟原始图像融合
Mat wshed;
addWeighted(image,0.4,PerspectiveImage,0.6,0,wshed);
imshow("AddWeighted Image",wshed);

waitKey(0);
}效果:



2.用鼠标选择mark的方法进行分水岭:
//////////////////////////用鼠标选择ROI进行分水岭
Mat img_Watershed = imread("D:/ImageTest/222.jpg",CV_LOAD_IMAGE_COLOR);
Mat temp_W;
Mat imgClone;
Point pt2_W;//记录鼠标左键按下时的坐标
int x_p,y_p;
bool flag_W = false;////flag用来标记鼠标移动是否画矩形
int numbers;
//1.定义标记图像markers
Mat markers2(img_Watershed.size(),CV_8UC1,Scalar(0));

///////////////////////////
void OnMouseW(int event, int x, int y, int flag_W, void* param);
void imgWatershedShow();//分水岭3

void imgWatershed3()//32分水岭3
{
numbers=0;
imgClone= img_Watershed.clone();
namedWindow("Watershed", CV_WINDOW_AUTOSIZE);
setMouseCallback("Watershed", OnMouseW, &imgClone); //设置鼠标回调函数
while(1)
{
imshow("Watershed",imgClone); //每10ms刷新一次图像,不在onMouse()里显示图像,因为太快会有拖动重影
if(27 == waitKey(10)) //Esc跳出循环
break;
}
}

void OnMouseW(int event, int x, int y, int flag_W, void* param)
{
switch(event)
{
case CV_EVENT_LBUTTONDOWN: //鼠标左键按下
//cout<<"left button down"<<endl;
flag_W = true;//flag标记置为真,触发鼠标移动画矩形
pt2_W.x = x; //记录当前点坐标
pt2_W.y = y;
break;
case CV_EVENT_MOUSEMOVE: //鼠标移动
//cout<<"mouse move"<<endl;
if(flag_W)
{
temp_W=imgClone.clone();
rectangle(temp_W, pt2_W, Point(x, y), Scalar(0, 0, 255), 2, 8);//画矩形
imshow("Watershed",temp_W);
}
break;
case CV_EVENT_LBUTTONUP: //鼠标左键弹起
flag_W = false;//flag标记置为假,关闭鼠标移动画矩形
rectangle(imgClone,pt2_W, Point(x, y), Scalar(0, 0, 255), 2, 8);
x_p=x;
y_p=y;
imgWatershedShow();
imshow("Watershed",imgClone); //显示
break;
default:
break;
}
}

void imgWatershedShow()//分水岭3
{
//1.定义标记图像markers
Mat markers(img_Watershed.size(),CV_8UC1,Scalar(0));

if(numbers<3)
{
if(numbers==0){
rectangle(markers,pt2_W, Point(x_p, y_p),Scalar(255),-1,8);
rectangle(markers2,pt2_W, Point(x_p, y_p),Scalar(255),-1,8);

////添加文字--背景色
QString str1="background";//qt方法
QByteArray cStr1 = str1.toLocal8Bit(); // 注意,这个QByteArray 对象一定要建立
char *p1 = cStr1.data();
putText(markers,p1, Point(30, 30),CV_FONT_HERSHEY_COMPLEX, 0.8, Scalar(255, 255, 255), 1, 8);
imshow("markers-input",markers);
}
if(numbers==1){
rectangle(markers, pt2_W, Point(x_p, y_p), Scalar(128), -1, 8);
rectangle(markers2, pt2_W, Point(x_p, y_p), Scalar(128), -1, 8);
////添加文字--第一个
QString str1="first";//qt方法
QByteArray cStr1 = str1.toLocal8Bit(); // 注意,这个QByteArray 对象一定要建立
char *p1 = cStr1.data();
putText(markers,p1, Point(30, 30),CV_FONT_HERSHEY_COMPLEX, 0.8, Scalar(255, 255, 255), 1, 8);
imshow("markers-input",markers);
}
if(numbers==2){
rectangle(markers, pt2_W, Point(x_p, y_p), Scalar(64), -1, 8);
rectangle(markers2, pt2_W, Point(x_p, y_p), Scalar(64), -1, 8);
////添加文字--第二个
QString str1="second";//qt方法
QByteArray cStr1 = str1.toLocal8Bit(); // 注意,这个QByteArray 对象一定要建立
char *p1 = cStr1.data();
putText(markers,p1, Point(30, 30),CV_FONT_HERSHEY_COMPLEX, 0.8, Scalar(255, 255, 255), 1, 8);
imshow("markers-input",markers);
}
numbers=numbers+1;

}
else
{
Mat copyImg=img_Watershed.clone();
//基于标记图像的分水岭算法
//1将markers2转换成32位单通道图像(分水岭函数要求)
markers2.convertTo(markers2,CV_32S);
watershed(copyImg,markers2); //分水岭算法
markers2.convertTo(markers2, CV_8UC1); //将markers转换成8位单通道
//2.提取轮廓并绘制轮廓
Mat mark1, mark2,mark3;
mark1 = markers2.clone();//提取轮廓1
mark2 = markers2.clone();//提取轮廓2
mark3 = markers2.clone();//提取背景轮廓
//阈值化,黑中找白
threshold(mark1,mark1,129,255,CV_THRESH_TOZERO_INV);
threshold(mark1,mark1,120,255,CV_THRESH_TOZERO);
//阈值化,黑中找白
threshold(mark2, mark2, 65, 255, CV_THRESH_TOZERO_INV);
//阈值化,黑中找白,找背景
threshold(mark3, mark3, 129, 255, CV_THRESH_BINARY);
imshow("mark1", mark1);
imshow("mark2", mark2);
imshow("mark3", mark3);
//寻找背景轮廓并绘制
vector<vector<Point>> contours3;
findContours(mark3, contours3, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
drawContours(copyImg, contours3, -1, Scalar(0, 0, 255), -1, 8);
//寻找第一轮廓并绘制
vector<vector<Point>> contours1;
findContours(mark1,contours1,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_NONE);
drawContours(copyImg,contours1,-1,Scalar(255,0,0),-1,8);
//寻找第二轮廓并绘制
vector<vector<Point>> contours2;
findContours(mark2, contours2, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
drawContours(copyImg, contours2, -1, Scalar(0, 255, 0), -1, 8);
imshow("copyImg",copyImg);
//3.与原图像叠加
Mat result = img_Watershed*0.5 + copyImg*0.5;
imshow("result", result);

numbers=0;
imgClone=img_Watershed.clone();
}
}
效果1.用鼠标选择3处:



选择第3处后再次选择将进行分水岭处理:





内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: