OpenCV——分水岭算法

一、分水岭算法

1、概述

  分水岭算法是一种图像分割常用的算法,可以有效地将图像中的目标从背景中分离出来。本文以OpenCV库中的分水岭算法为基础,介绍图像分割中的常用概念和算法原理,并结合实际案例展示分水岭算法的应用。

2、图像分割概念

  图像分割指的是将图像分成多个不同的区域或对象的过程。图像分割是计算机视觉和图像处理领域中的重要问题,包括物体检测、形状分析、三维重建、医学图像处理等众多应用。对于图像分割,有四种典型的方法:

  • 阈值分割:基于给定的阈值将图像分成两个区域。

  • 区域生长:从种子像素开始生长,合并与种子相邻的像素来形成区域。

  • 边缘检测:通过检测图像中的边缘,将图像分割成不同的对象。

  • 基于聚类的方法:利用聚类算法将图像分成多个区域。

3、分水岭算法原理

  分水岭算法是一种基于图论的图像分割算法,它将图像看成一个拓扑图,把亮度值看成高度,水从高处向低处流动,在高处建立分界线,将图像分割成多个区域。分水岭算法包含以下四个步骤:

  1. 载入图像并转化为灰度图像。

  2. 对灰度图像进行形态学变换,以抑制图像中的噪声和平滑图像。

  3. 计算距离变换,找到不同区域之间的分界线,将其看成浸没的水平面。

  4. 利用分界线将图像分成多个区域。

二、主要函数

  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】图像分割——分水岭算法

猜你喜欢

转载自blog.csdn.net/qq_36686437/article/details/131357062