1、前言
图像分割是图像理解领域关注的一个热点,图像分割是图像分析的第一步,是计算机视觉的基础,是图像理解的重要组成部分。根据灰度、彩色、空间纹理、几何形状等特征把图像划分成若干个互不相交的区域,使得这些特征在同一区域内表现出一致性或相似性,而在不同区域间表现出明显的不同。简单的说就是在一副图像中,把目标从背景中分离出来。对于灰度图像来说,区域内部的像素一般具有灰度相似性,而在区域的边界上一般具有灰度不连续性。
分水岭算法是根据分水岭的构成来考虑图像的分割,是一种基于拓扑理论的数学形态学的分割方法,其基本思想是把图像看作是测地学上的拓扑地貌,图像中每一点像素的灰度值表示该点的海拔高度,每一个局部极小值及其影响区域称为集水盆,而集水盆的边界则形成分水岭。
分水岭对微弱边缘具有良好的响应,图像中的噪声、物体表面细微的灰度变化都有可能产生过度分割的现象,但是这也同时能够保证得到封闭连续边缘。同时,分水岭算法得到的封闭的集水盆也为分析图像的区域特征提供了可能。
2、相关函数
(1)distanceTransform()距离变换
计算图像中每一个非零点距离离自己最近的零点的距离。
void distanceTransform(
InputArray src, // 源图
OutputArray dst,// 保存了每一个点与最近的零点的距离信息,图像上越亮的点,代表了离零点的距离越远。
int distanceType, // 计算距离的方法
int maskSize,
int dstType=CV_32F
);
int distanceType:
CV_DIST_USER =-1,
CV_DIST_L1 =1,
CV_DIST_L2 =2,
CV_DIST_C =3,
CV_DIST_L12 =4,
CV_DIST_FAIR =5,
CV_DIST_WELSCH =6,
CV_DIST_HUBER =7
(1)watershed()分水岭算法函数
void watershed(
InputArray image, 第一个参数 image,必须是一个8bit 3通道彩色图像。
InputOutputArray markers // 包含不同区域的轮廓,每个轮廓有一个自己唯一的编号。
);
3、处理过程
(1). 图像灰度化、滤波、锐化、边沿提取
(2)计算距离
(3). 查找轮廓,并且把轮廓信息按照不同的编号绘制到watershed的第二个入参merkers上,相当于标记注水点。
(4). watershed分水岭运算
(5). 绘制分割出来的区域。
4、代码
// 第1步,灰度图
cvtColor(srcImg1, grayImg, CV_BGR2GRAY);
// 第2步 锐化
Mat kernel = (Mat_<float>(3, 3) << 1, 1, 1, 1, -9, 1 , 1, 1,1);
Mat laplance;
Mat sharp;
filter2D(srcImg1, laplance, CV_32F, kernel, Point(-1, -1), 0, BORDER_DEFAULT);
// 第3步 边沿提取
srcImg1.convertTo(sharp, CV_32F);
Mat result = sharp - laplance;
result.convertTo(result, CV_8UC3);
// 二值化
Mat binImg;
cvtColor(result, result, CV_BGR2GRAY);
threshold(result, binImg, 180, 255, THRESH_BINARY|THRESH_OTSU);
// 第4步 距离计算
Mat distanceImg;
distanceTransform(binImg, distanceImg, CV_DIST_L1, 3);
normalize(distanceImg, distanceImg, 0, 1, NORM_MINMAX);
imshow("distanceTransform ", distanceImg);
threshold(distanceImg, distanceImg, 0.01, 1, THRESH_BINARY);
Mat m1 = getStructuringElement(MORPH_RECT, Size(5, 5), Point(-1, -1));
Mat erodeImg;
erode(distanceImg, erodeImg, m1);
// 标记
Mat u8Img;
erodeImg.convertTo(u8Img, CV_8U);
// 第5步 轮廓提取
vector<vector<Point>> ptContours;
vector<Vec4i> hierarchy;
findContours(u8Img, ptContours, hierarchy, RETR_LIST, CHAIN_APPROX_SIMPLE, Point(0, 0));
Mat markers = Mat::ones(u8Img.size(), CV_32SC1);
for (int i = 0; i < ptContours.size(); i++)
{
// 画轮廓线
drawContours(markers,
ptContours,
i,
Scalar(255, 255, 255),
2
);
}
// 第6步 分水岭分割
watershed(srcImg1, markers);
Mat markImg= Mat::zeros(markers.size(), CV_8UC1);
markers.convertTo(markImg, CV_8UC1);
imshow("markImg ", markImg);
5、完整代码下载
本文源码在Debug–x64下编译运行。
ZIP包中包含开发环境,下载解压可直接编译运行。