- 操作系统: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 ); // 分水岭区域elsedst.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;}