自学内容网 自学内容网

OpenCV图像分割(2)分水岭算法图像分割函数watershed()的使用

  • 操作系统: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;
 }

运行结果

在这里插入图片描述


原文地址:https://blog.csdn.net/jndingxin/article/details/142487155

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!