iis服务器助手广告广告
返回顶部
首页 > 资讯 > 精选 >OpenCV基于分水岭算法的图像分割怎么实现
  • 696
分享到

OpenCV基于分水岭算法的图像分割怎么实现

2023-07-05 05:07:46 696人浏览 独家记忆
摘要

本文小编为大家详细介绍“OpenCV基于分水岭算法的图像分割怎么实现”,内容详细,步骤清晰,细节处理妥当,希望这篇“OpenCV基于分水岭算法的图像分割怎么实现”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。1.

本文小编为大家详细介绍“OpenCV基于分水岭算法的图像分割怎么实现”,内容详细,步骤清晰,细节处理妥当,希望这篇“OpenCV基于分水岭算法的图像分割怎么实现”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。

1. 分水岭算法

分水岭分割可以通过使用 cv::watershed 函数实现,函数的输入是一个 32 位有符号整数标记图像,其中每个非零像素表示一个标签。通过标记图像中已知属于给定区域的一些像素,利用初始标记,分水岭算法可以确定其他像素所属的区域。

(1) 首先,将标记图像读取为灰度图像,然后将其转换为整数类型:

class WatershedSegmentater {    private:        cv::Mat markers;    public:        void setMarkers(const cv::Mat& markerImage) {            // 转换数据类型            markerImage.convertTo(markers, CV_32S);        }        cv::Mat process(const cv::Mat& image) {            // 应用分水岭算法            cv::watershed(image, markers);            return markers;        }

有多种获取标记的方式,例如,使用预处理步骤识别出属于感兴趣对象的某些像素,然后利用分水岭算法根据初始标记分割完整的对象。在本节中,我们将使用二值图像来识别相应原始图像中的动物。因此,从二值图像中,我们需要识别属于前景(动物)的像素和属于背景(主要是雪地)的像素,我们用标签 255 标记前景像素,用标签 128 标记背景像素,其他像素则标记为 0

(2) 初始二值图像包含过多属于图像各个部分的白色像素,为了只保留属于重要对象的像素,我们首先需要腐蚀该图像:

// 消除噪音cv::Mat fg;cv::erode(binary, fg, cv::Mat(), cv::Point(-1, -1), 4);

结果如下图所示:

OpenCV基于分水岭算法的图像分割怎么实现

(3) 图中仍然存在一些属于背景(雪地)的像素,我们通过对原始二值图像进行膨胀来选择几个属于背景的像素:

// 标记图像像素cv::Mat bg;cv::dilate(binary, bg, cv::Mat(), cv::Point(-1, -1), 4);cv::threshold(bg, bg, 1, 128, cv::THRESH_BINARY_INV);

结果如下图所示,黑色像素对应于背景像素:

OpenCV基于分水岭算法的图像分割怎么实现

(4) 将这些图像组合起来形成标记图像:

cv::Mat markers(binary.size(), CV_8U, cv::Scalar(0));markers = fg+bg;

我们使用重载的 + 运算符来组合图像,得到用作分水岭算法的输入:

OpenCV基于分水岭算法的图像分割怎么实现

(5) 在这个输入图像中,白色区域属于前景对象,灰色区域是背景的一部分,黑色区域则属于未知标签,得到分割结果如下:

// 创建分水岭分割对象WatershedSegmentater segmenter;segmenter.setMarkers(markers);segmenter.process(image);

更新标记图像,以便为黑色区域中的像素重新分配标签,而属于边界的像素的值为 -1。结果标签图像如下:

OpenCV基于分水岭算法的图像分割怎么实现

图像中对象边缘的可视化结果如下图所示:

OpenCV基于分水岭算法的图像分割怎么实现

2. 分水岭算法直观理解

我们使用拓扑图进行类比,为了创建分水岭分割,我们从级别 0 开始注水,随着水位逐渐增加,就形成了集水盆地。这些盆地的大小也会逐渐增加,两个不同盆地的水最终会汇合,发生这种情况时,会创建一个分水岭,以将两个盆地分开。一旦水位达到最高水位,这些水域和分水岭就形成了分水岭分割。

在注水过程中最初会产生许多小盆地,当这些盆地进行合并时,会创建许多分水岭线,从而导致图像被过度分割。为了克服这个问题,已经提出了多种改进算法,在 OpenCV 调用 cv::watershed 函数时,注水过程从一组预定义的标记像素开始,根据分配给初始标记的值对盆地进行标记,当具有相同标签的两个盆地合并时,不会创建分水岭,从而防止过度分割,更新输入标记图像以获得最终的分水岭分割。用户可以输入带有任意数量的标签和未知标签的标记图像,标记图像的像素类型为为 32 位有符号整数,以便能够定义超过 255 个标签。cv::watershed 函数还允许返回与分水岭关联的像素(使用特殊值 -1 进行标记)。

为了便于显示结果,我们引入两种特殊的方法。第一个方法 getSegmentation() 通过阈值返回标签图像,分水岭值为 0

// 返回结果cv::Mat getSegmentation() {    cv::Mat tmp;    markers.convertTo(tmp, CV_8U);    return tmp;}

第二种方法 getWatersheds() 返回的图像中,分水岭线使用值 0 进行标记,图像的其余部分像素值为 255,可以使用 cv::convertTo 方法实现:

// 返回分水岭cv::Mat getWatersheds() {    cv::Mat tmp;    markers.convertTo(tmp,CV_8U,255,255);    return tmp;}

在转换之前应用线性变换,可以将像素值 -1 转换为 0 ( − 1 × 255 + 255 = 0 -1\times 255+255=0 −1×255+255=0)。由于将有符号整数转换为无符号字符时需应用饱和操作,大于 255 的像素值将转换为 255

我们也可以通过许多不同的方式获得标记图像。例如,可以令用户以交互方式在图像中标记属于对象和背景的像素区域;或者,如果我们需要识别位于图像中心的物体,可以输入一个中心区域标有特定标签的图像,且图像背景标记带有另一个标签,可以按以下方式创建标记图像:

// 标记背景像素cv::Mat imageMask(image.size(), CV_8U, cv::Scalar(0));cv::rectangle(imageMask,            cv::Point(5, 5),            cv::Point(image.cols-5, image.rows-5),            cv::Scalar(255),            3);// 标记前景像素cv::rectangle(imageMask,            cv::Point(image.cols/2-10, image.rows/2-10),            cv::Point(image.cols/2+10, image.rows/2+10),            cv::Scalar(1),            10);

3. 完整代码

头文件 (watershedSegmentation.h) 完整代码如下:

#if !defined WATERSHS#define WATERSHS#include <opencv2/core/core.hpp>#include <opencv2/imgproc/imgproc.hpp>class WatershedSegmentater {    private:        cv::Mat markers;    public:        void setMarkers(const cv::Mat& markerImage) {            // 转换数据类型            markerImage.convertTo(markers, CV_32S);        }        cv::Mat process(const cv::Mat& image) {            // 应用分水岭算法            cv::watershed(image, markers);            return markers;        }        // 返回结果        cv::Mat getSegmentation() {            cv::Mat tmp;            markers.convertTo(tmp, CV_8U);            return tmp;        }        // 返回分水岭        cv::Mat getWatersheds() {            cv::Mat tmp;            markers.convertTo(tmp,CV_8U,255,255);            return tmp;        }};#endif

主文件 (segment.cpp) 完整代码如下所示:

#include <iOStream>#include <opencv2/core/core.hpp>#include <opencv2/highgui/highgui.hpp>#include <opencv2/imgproc/imgproc.hpp>#include "watershedSegmentation.h"int main() {    // 读取输入图像    cv::Mat image = cv::imread("1.png");    if (!image.data) return 0;    cv::namedWindow("Original Image");    cv::imshow("Original Image",image);    // 读取二值图像    cv::Mat binary;    binary = cv::imread("binary.png", 0);    cv::namedWindow("Binary Image");    cv::imshow("Binary Image", binary);    // 消除噪音    cv::Mat fg;    cv::erode(binary, fg, cv::Mat(), cv::Point(-1, -1), 4);    cv::namedWindow("Foreground Image");    cv::imshow("Foreground Image", fg);    // 标记图像像素    cv::Mat bg;    cv::dilate(binary, bg, cv::Mat(), cv::Point(-1, -1), 4);    cv::threshold(bg, bg, 1, 128, cv::THRESH_BINARY_INV);    cv::namedWindow("Background Image");    cv::imshow("Background Image", bg);    cv::Mat markers(binary.size(), CV_8U, cv::Scalar(0));    markers = fg+bg;    cv::namedWindow("Markers");    cv::imshow("Markers", markers);    // 创建分水岭分割对象    WatershedSegmentater segmenter;    segmenter.setMarkers(markers);    segmenter.process(image);    cv::namedWindow("Segmentation");    cv::imshow("Segmentation", segmenter.getSegmentation());    cv::namedWindow("Watersheds");    cv::imshow("Watersheds", segmenter.getWatersheds());    // 打开另一张图像    image = cv::imread("3.png");    // 标记背景像素    cv::Mat imageMask(image.size(), CV_8U, cv::Scalar(0));    cv::rectangle(imageMask,                cv::Point(5, 5),                cv::Point(image.cols-5, image.rows-5),                cv::Scalar(255),                3);    // 标记前景像素    cv::rectangle(imageMask,                cv::Point(image.cols/2-10, image.rows/2-10),                cv::Point(image.cols/2+10, image.rows/2+10),                cv::Scalar(1),                10);    segmenter.setMarkers(imageMask);    segmenter.process(image);    cv::rectangle(image,                cv::Point(5, 5),                cv::Point(image.cols-5, image.rows-5),                cv::Scalar(255, 255, 255),                3);    cv::rectangle(image,                cv::Point(image.cols/2-10, image.rows/2-10),                cv::Point(image.cols/2+10, image.rows/2+10),                cv::Scalar(1, 1, 1),                10);    cv::namedWindow("Image with marker");    cv::imshow("Image with marker", image);    cv::namedWindow("Watershed");    cv::imshow("Watershed", segmenter.getWatersheds());    cv::waiTKEy();    return 0;}

读到这里,这篇“OpenCV基于分水岭算法的图像分割怎么实现”文章已经介绍完毕,想要掌握这篇文章的知识点还需要大家自己动手实践使用过才能领会,如果想了解更多相关内容的文章,欢迎关注编程网精选频道。

--结束END--

本文标题: OpenCV基于分水岭算法的图像分割怎么实现

本文链接: https://www.lsjlt.com/news/349695.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • OpenCV基于分水岭算法的图像分割怎么实现
    本文小编为大家详细介绍“OpenCV基于分水岭算法的图像分割怎么实现”,内容详细,步骤清晰,细节处理妥当,希望这篇“OpenCV基于分水岭算法的图像分割怎么实现”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。1. ...
    99+
    2023-07-05
  • OpenCV实战记录之基于分水岭算法的图像分割
    目录0. 前言1. 分水岭算法2. 分水岭算法直观理解3. 完整代码总结0. 前言 分水岭变换是一种流行的图像处理算法,用于快速将图像分割成同质区域。分水岭变换主要基于以下思想:当图...
    99+
    2023-02-22
    opencv 分水岭算法 opencv图像分割算法 opencv 分水岭算法应用
  • C++中实现OpenCV图像分割与分水岭算法
    分水岭算法是一种图像区域分割法,在分割的过程中,它会把跟临近像素间的相似性作为重要的参考依据,从而将在空间位置上相近并且灰度值相近的像素点互相连接起来构成一个封闭的轮廓,封闭性是分水...
    99+
    2022-11-12
  • C++中怎么实现OpenCV图像分割与分水岭算法
    小编给大家分享一下C++中怎么实现OpenCV图像分割与分水岭算法,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!分水岭算法是一种图像区域分割法,在分割的过程中,它...
    99+
    2023-06-15
  • OpenCV基于距离变换和分水岭实现图像分割
    目录一.图像分割二.基于距离变换和分水岭的图像分割代码实现图像处理效果一.图像分割 图像分割是根据灰度、颜色、纹理和形状等特征,把图像分成若干个特定的、具有独特性质的区域,这些特征在...
    99+
    2022-11-13
  • Python基于均值漂移算法和分水岭算法实现图像分割
    目录一.基于均值漂移算法的图像分割二.基于分水岭算法的图像分割三.总结一.基于均值漂移算法的图像分割 均值漂移(Mean Shfit)算法是一种通用的聚类算法,最早是1975年Fuk...
    99+
    2023-01-11
    Python均值漂移算法 图像分割 Python 分水岭算法 图像分割 Python图像分割
  • OpenCV-Python使用分水岭算法实现图像的分割与提取
    目录图像分割分水岭算法waterShed函数形态学分割distanceTransform函数确定未知区域ConnectedComponents函数实战分水岭算法随着当今世界的发展,计...
    99+
    2022-11-12
  • OpenCV图像分割之分水岭算法与图像金字塔算法详解
    目录前言一、使用分水岭算法分割图像1、cv2.distanceTransform()函数2、cv2.connectedComponents()函数3、cv2.watershed()函...
    99+
    2022-11-12
  • Python实现基于标记的分水岭分割算法
    目录1. 原理2.代码实现2.1 利用OpenCV和c++实现分水岭算法2.2 Python实现分水岭分割(1)2.3 Python实现分水岭分割(2)分水岭技术是一种众所周知的分割...
    99+
    2022-11-11
  • OpenCV-Python怎么使用分水岭算法实现图像分割与提取功能
    小编给大家分享一下OpenCV-Python怎么使用分水岭算法实现图像分割与提取功能,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!随着当今世界的发展,计算机视觉技...
    99+
    2023-06-15
  • 基于OpenCV实现图像分割
    本文实例为大家分享了基于OpenCV实现图像分割的具体代码,供大家参考,具体内容如下 1、图像阈值化 源代码: #include "opencv2/highgui/highgui...
    99+
    2022-11-12
  • Python+OpenCV实现分水岭分割算法的示例代码
    目录前言1.使用分水岭算法进行分割2.Watershed与random walker分割对比前言 分水岭算法是用于分割的经典算法,在提取图像中粘连或重叠的对象时特别有用,例如下图中的...
    99+
    2022-11-11
  • OpenCV图像算法怎么实现图像切分图像合并
    本篇内容介绍了“OpenCV图像算法怎么实现图像切分图像合并”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!将一张图片切分成多个小图片并将小图...
    99+
    2023-06-30
  • 详解PythonOpenCV图像分割算法的实现
    目录前言1.图像二值化2.自适应阈值分割算法3.Otsu阈值分割算法4.基于轮廓的字符分离4.1轮廓检测 4.2轮廓绘制4.3包围框获取4.4矩形绘制 前言 图像...
    99+
    2022-11-11
  • Python基于纹理背景和聚类算法实现图像分割详解
    目录一.基于纹理背景的图像分割二.基于K-Means聚类算法的区域分割三.总结一.基于纹理背景的图像分割 该部分主要讲解基于图像纹理信息(颜色)、边界信息(反差)和背景信息的图像分割...
    99+
    2023-01-03
    Python 纹理 图像分割 Python 聚类算法 图像分割 Python图像分割
  • 基于OpenCV(python)的实现文本分割之垂直投影法
    在我的上一篇博客中讲述了水平投影法取出文本行图像的实现,在这里将用垂直投影法对文本行的每个字符进行分割。下图是用水平投影法切割的文本行: 文本分割的原理如下,先用水平投影取出单一文...
    99+
    2022-11-11
  • 怎么使用Python第三方opencv库实现图像分割处理
    这篇文章主要介绍了怎么使用Python第三方opencv库实现图像分割处理的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇怎么使用Python第三方opencv库实现图像分割处理文章都会有所收获,下面我们一起来看...
    99+
    2023-07-02
  • python基于OpenCV模块实现视频流数据切割为图像帧数据(流程分析)
    动态视频流数据的处理可以转化为静态图像帧的处理,这样就可以在不改动图像模型的情况下实现视频流数据的处理工作,当然视频流数据也可以采用视频的处理方法来直接处理,这里今天主要是实践一下视...
    99+
    2022-11-11
  • matlab怎么实现图像的自适应多阈值快速分割
    今天小编给大家分享一下matlab怎么实现图像的自适应多阈值快速分割的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下...
    99+
    2022-10-19
  • Python基于决策树算法的分类预测怎么实现
    今天小编给大家分享一下Python基于决策树算法的分类预测怎么实现的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。一、决策树的...
    99+
    2023-06-26
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作