1.分水岭方法介绍
OpenCV 提供了分水岭算法(Watershed Algorithm)的实现, 使用分水岭算法对图像进行分割,将图像的不同区域分割成互不干扰的区域。分水岭算法模拟了水在图像中的扩散和聚集过程,将标记的边界被看作是阻挡水扩散的高山,通过模拟水的扩散和聚集,最终确定图像的分割边界。
如图所示:水从山低蔓延,而高山阻止水扩散。
2.分水岭法分割图像函数watershed()
void cv::watershed ( InputArray image,
InputOutputArray markers
)
- image:输入图像,数据类型为CV_8U的三通道图像
- markers:输入/输出CV_32S的单通道图像的标记结果,与原图像具有相同的尺寸
示例代码:
基于标记图像中画的线来对原图像进行分割。
void watershed_f(Mat mat,Mat mat2){//mat原图像 mat2含有标记的图像
// 把四通道原图像转换成三通道
Mat image;
cv::cvtColor(mat, image, cv::COLOR_BGRA2BGR);
Mat imgGray,imgMask,img_;
Mat maskWaterShed;//watershed()函数的参数
//对标记的图像进行灰度化
Mat image2;
cvtColor(mat2,imgGray,COLOR_BGR2GRAY);
//对标记的图像二值化并开运算,可得到标记画的线
threshold(imgGray,imgMask,250,255,THRESH_BINARY);
Mat k= getStructuringElement(0,Size(3,3));
morphologyEx(imgMask,imgMask,MORPH_OPEN,k);
//显示二值化并开运算的结果
imwrite("/sdcard/DCIM/imgMask2.png",imgMask);
//对二值化后的标记图像进行轮廓检测,可得到画的线的轮廓
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(imgMask,contours,hierarchy,RETR_TREE,CHAIN_APPROX_SIMPLE,Point());
//在maskWaterShed上绘制出上面得到的轮廓
maskWaterShed=Mat::zeros(imgMask.size(),CV_32S);
for(int index=0;index<contours.size();index++){
drawContours(maskWaterShed,contours,index,Scalar::all(index+1),2,8);
}
//分水岭算法 需要对原图像进行处理
watershed(image,maskWaterShed);
//显示分水岭算法分割的每个区域
imwrite("/sdcard/DCIM/maskWaterShed2.png",maskWaterShed);
//随机生成几种颜色
vector<Vec3b> colors;
for(int i=0;i<contours.size();i++){
int b=theRNG().uniform(0,255);
int g=theRNG().uniform(0,255);
int r=theRNG().uniform(0,255);
colors.push_back(Vec3b((uchar)b,(uchar)g,(uchar)r));
}
//给分水岭算法分割的每个区域添加颜色
Mat resultImg=Mat(image2.size(),CV_8UC3);
for(int i=0;i<imgMask.rows;i++){
for(int j=0;j<imgMask.cols;j++){
//绘制每个区域的颜色
int index=maskWaterShed.at<int>(i,j);
if(index==-1)//区域间的值被置为-1(边界)
{
resultImg.at<Vec3b>(i,j)=Vec3b(255,255,255);
}
else if(index<=0||index>contours.size())//没有标记清楚的区域被置为0
{
resultImg.at<Vec3b>(i,j)=Vec3b(0,0,0);
}else{
resultImg.at<Vec3b>(i,j)=colors[index-1];
}
}
}
//显示给分水岭算法分割的每个区域添加颜色的结果
imwrite("/sdcard/DCIM/resultImg2.png",resultImg);
//分割的区域与原图像结合
resultImg=resultImg*0.8+image*0.2;
imwrite("/sdcard/DCIM/resultImg3.png",resultImg);
}