目录
一、分水岭算法
1、概述
分水岭算法是一种图像分割常用的算法,可以有效地将图像中的目标从背景中分离出来。本文以OpenCV库中的分水岭算法为基础,介绍图像分割中的常用概念和算法原理,并结合实际案例展示分水岭算法的应用。
2、图像分割概念
图像分割指的是将图像分成多个不同的区域或对象的过程。图像分割是计算机视觉和图像处理领域中的重要问题,包括物体检测、形状分析、三维重建、医学图像处理等众多应用。对于图像分割,有四种典型的方法:
-  
阈值分割:基于给定的阈值将图像分成两个区域。
 -  
区域生长:从种子像素开始生长,合并与种子相邻的像素来形成区域。
 -  
边缘检测:通过检测图像中的边缘,将图像分割成不同的对象。
 -  
基于聚类的方法:利用聚类算法将图像分成多个区域。
 
3、分水岭算法原理
分水岭算法是一种基于图论的图像分割算法,它将图像看成一个拓扑图,把亮度值看成高度,水从高处向低处流动,在高处建立分界线,将图像分割成多个区域。分水岭算法包含以下四个步骤:
-  
载入图像并转化为灰度图像。
 -  
对灰度图像进行形态学变换,以抑制图像中的噪声和平滑图像。
 -  
计算距离变换,找到不同区域之间的分界线,将其看成浸没的水平面。
 -  
利用分界线将图像分成多个区域。
 
二、主要函数
  cv::watershed 函数实现了基于距离变换的分水岭算法。该函数的原型如下:
void watershed(InputArray image, 
               InputOutputArray markers
               );
 
image:输入的图像,必须为8位的3通道彩色图像。markers:输出的标记图像,必须为单通道32位整型图像。
在使用cv::watershed函数进行分水岭算法分割时,需要先进行前期处理,包括图像的预处理和创建标记图像。
三、C++代码
#include <opencv2\opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main()
{
	Mat img, imgGray, imgMask;
	Mat maskWaterShed;  // watershed()函数的参数
	img = imread("HoughLines.jpg");  //原图像
	if (img.empty())
	{
		cout << "请确认图像文件名称是否正确" << endl;
		return -1;
	}
	cvtColor(img, imgGray, COLOR_BGR2GRAY);
	//提取边缘并进行闭运算
	Canny(imgGray, imgMask, 150, 300);
	Mat k = getStructuringElement(0, Size(3, 3));
	morphologyEx(imgMask, imgMask, MORPH_CLOSE, k);
	imshow("边缘图像", imgMask);
	imshow("原图像", img);
	//计算连通域数目
	vector<vector<Point>> contours;
	vector<Vec4i> hierarchy;
	findContours(imgMask, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);
	//在maskWaterShed上绘制轮廓,用于输入分水岭算法
	maskWaterShed = Mat::zeros(imgMask.size(), CV_32S);
	for (int index = 0; index < contours.size(); index++)
	{
		drawContours(maskWaterShed, contours, index, Scalar::all(index + 1),
			-1, 8, hierarchy, INT_MAX);
	}
	//分水岭算法   需要对原图像进行处理
	watershed(img, 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(img.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  // 其他每个区域的值保持不变:1,2,…,contours.size()
			{
				resultImg.at<Vec3b>(i, j) = colors[index - 1];  // 把些区域绘制成不同颜色
			}
		}
	}
	resultImg = resultImg * 0.6 + img * 0.4;
	imshow("分水岭结果", resultImg);
	waitKey(0);
	return 0;
}
 
四、结果展示
1、原始图像

2、分割结果

五、参考链接
[1] 【OpenCv】图像分割——分水岭算法









