- 操作系统:ubuntu22.04
- OpenCV版本:OpenCV4.9
- IDE:Visual Studio Code
- 编程语言:C++11
算法描述
使用分水岭算法执行基于标记的图像分割。
该函数实现了分水岭算法的一种变体,即无参数基于标记的分割算法,在文献 [186] 中有描述。
在将图像传递给函数之前,你需要在图像的标记中大致勾勒出所期望的区域,并使用正数(>0)索引。因此,每一个区域表现为一个或多个连接组件,其像素值为1、2、3等。这样的标记可以从二值掩模中通过 findContours 和 drawContours 获取(参见watershed.cpp演示)。标记是未来图像区域的“种子”。标记中的所有其他像素,即那些与轮廓区域的关系未知且应由算法定义的像素,应设置为0。在函数的输出中,标记中的每一个像素都被设置为“种子”组件的值,或者在区域之间的边界处设置为-1。
OpenCV 中的 watershed() 函数是一种用于图像分割的技术,它基于分水岭算法来分割图像中的不同区域。分水岭算法通常用于分割那些彼此紧密相连的对象,特别是当对象之间没有清晰的边界时。
注意
任何两个相邻的连通组件不一定被分水岭边界(-1的像素)分隔;例如,它们可以在传递给函数的初始标记图像中相互接触
函数原型
void cv::watershed
(
InputArray image,
InputOutputArray markers
)
参数
- 参数mage 输入的8位3通道图像。
- 参数markers 输入/输出的32位单通道图像(标记地图)。它应该与image具有相同的尺寸。
代码示例
#include <iostream>
#include <opencv2/opencv.hpp>
int main()
{
// 读取图像
cv::Mat img = cv::imread( "/media/dingxin/data/study/OpenCV/sources/images/fruit_small.jpg" );
if ( img.empty() )
{
std::cout << "Could not open or find the image!" << std::endl;
return -1;
}
// 转换为灰度图像
cv::Mat gray;
cvtColor( img, gray, cv::COLOR_BGR2GRAY );
// 二值化处理
cv::Mat thresh;
threshold( gray, thresh, 0, 255, cv::THRESH_BINARY_INV + cv::THRESH_OTSU );
// 噪声去除
cv::Mat kernel = cv::getStructuringElement( cv::MORPH_ELLIPSE, cv::Size( 3, 3 ) );
morphologyEx( thresh, thresh, cv::MORPH_OPEN, kernel, cv::Point( -1, -1 ), 2 );
// 距离变换
cv::Mat dist;
distanceTransform( thresh, dist, cv::DIST_L2, 3 );
normalize( dist, dist, 0, 1.0, cv::NORM_MINMAX );
// 标记生成
cv::Mat markers = cv::Mat::zeros( dist.size(), CV_32S );
cv::Mat dist8u;
dist.convertTo( dist8u, CV_8U );
std::vector< std::vector< cv::Point > > contours;
std::vector< cv::Vec4i > hierarchy;
findContours( dist8u, contours, hierarchy, cv::RETR_CCOMP, cv::CHAIN_APPROX_SIMPLE );
// 标记轮廓
for ( size_t i = 0; i < contours.size(); i++ )
{
cv::drawContours( markers, contours, static_cast< int >( i ), static_cast< int >( i + 1 ), -1 );
}
// 设置背景区域
cv::Mat marker32 = markers.clone();
cv::Mat markerAc = cv::Mat::zeros( markers.size(), CV_8U );
for ( int i = 0; i < img.rows; i++ )
{
for ( int j = 0; j < img.cols; j++ )
{
if ( marker32.at< int >( i, j ) == -1 )
{
markerAc.at< uchar >( i, j ) = 255;
}
}
}
cv::morphologyEx( markerAc, markerAc, cv::MORPH_CLOSE, kernel );
// 应用分水岭算法
cv::watershed( img, markers );
// 处理结果
std::vector< cv::Vec3b > colors( 256 );
for ( int i = 0; i < colors.size(); i++ )
{
colors[ i ][ 0 ] = static_cast< unsigned char >( ( i * 127 / 255 ) % 255 );
colors[ i ][ 1 ] = static_cast< unsigned char >( ( i * 251 / 255 ) % 255 );
colors[ i ][ 2 ] = static_cast< unsigned char >( ( i * 237 / 255 ) % 255 );
}
cv::Mat dst = cv::Mat::zeros( markers.size(), CV_8UC3 );
for ( int i = 0; i < markers.rows; i++ )
{
for ( int j = 0; j < markers.cols; j++ )
{
int index = markers.at< int >( i, j );
if ( index == -1 )
dst.at< cv::Vec3b >( i, j ) = cv::Vec3b( 0, 0, 255 ); // 分水岭区域
else
dst.at< cv::Vec3b >( i, j ) = colors[ index ];
}
}
// 显示结果
cv::imshow( "Original Image", img );
cv::imshow( "Threshold Image", thresh );
cv::imshow( "Markers Before Watershed", markerAc );
cv::imshow( "Segmented Image", dst );
cv::waitKey( 0 );
return 0;
}