目录
- 一、分水岭算法
- 1、概述
- 2、图像分割概念
- 3、分水岭算法原理
- 二、主要函数
- 三、C++代码
- 四、结果展示
- 1、原始图像
- 2、分割结果
- 五、参考链接
一、分水岭算法
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】图像分割——分水岭算法