【OpenCv】图像分割——分水岭算法

article/2025/10/16 5:43:01

文章目录

  • 1 原理
  • 2 算法改进
  • 3 API
  • 4 实例

1 原理

  分水岭分割方法,是一种基于拓扑理论的数学形态学的分割方法,其基本思想是把图像看作是测地学上的拓扑地貌,图像中每一点像素的灰度值表示该点的海拔高度,每一个局部极小值及其影响区域称为集水盆,而集水盆的边界则形成分水岭。分水岭的概念和形成可以通过模拟浸入过程来说明。在每一个局部极小值表面,刺穿一个小孔,然后把整个模型慢慢浸入水中,随着浸入的加深,每一个局部极小值的影响域慢慢向外扩展,在两个集水盆汇合处构筑大坝,即形成分水岭。这种方法也称作泛洪法,对应的还有降雨法。

在这里插入图片描述
  分水岭的计算过程是一个迭代标注过程。分水岭比较经典的计算方法是L. Vincent提出的。在该算法中,分水岭计算分两个步骤,一个是排序过程,一个是淹没过程。首先对每个像素的灰度级进行从低到高排序,然后在从低到高实现淹没过程中,对每一个局部极小值在 h h h 阶高度的影响域采用先进先出(FIFO)结构进行判断及标注。具体流程如下:

  • 把梯度图像中的像素按照灰度值进行分类,设定一个测地距离阈值(测地线距离(Geodesic Distance):地球表面两点之间的最短路径的距离)。
  • 找到灰度值最小的像素点,让 t h r e s h o l d threshold threshold 从最小值开始增长,这些点为起始点。
  • 水平面在增长的过程中,会碰到周围的邻域像素,测量这些像素到起始点(灰度值最低点)的测地距离,如果小于设定阈值,则将这些像素淹没,否则在这些像素上设置大坝,这样就对这些邻域像素进行了分类。
  • 随着水平面越来越高,会设置更多更高的大坝,直到灰度值的最大值,所有区域都在分水岭线上相遇,这些大坝就对整个图像像素的进行了分区。
    在这里插入图片描述

2 算法改进

  基于梯度图像的直接分水岭算法容易导致图像的过分割,产生这一现象的原因主要是由于输入的图像存在过多的极小区域而产生许多小的集水盆地,从而导致分割后的图像不能将图像中有意义的区域表示出来。

在这里插入图片描述
O p e n C v OpenCv OpenCv 中算法不从最小值开始增长,可以将相对较高的灰度值像素作为起始点(需要用户手动标记),从标记处开始进行淹没,则很多小区域都会被合并为一个区域,这被称为基于图像标(mark)的分水岭算法。其中标记的每个点就相当于分水岭中的注水点,从这些点开始注水使得水平面上升。手动标记太麻烦,我们可是使用距离转换(cv2.distanceTransform函数)的方法进行标记。cv2.distanceTransform计算的是图像内非零值像素点到最近的零值像素点的距离,即计算二值图像中所有像素点距离其最近的值为 0 的像素点的距离。当然,如果像素点本身的值为 0,则这个距离也为 0。在这里插入图片描述

3 API

cv2.watershed( InputArray image, InputOutputArray markers )
  • 第一个参数 i m a g e image image,必须是一个8bit 3通道彩色图像矩阵序列。
  • 关键是第二个参数 markers:在执行分水岭函数watershed之前,必须对第二个参数 m a r k e r s markers markers 进行处理,它应该包含不同区域的轮廓,每个轮廓有一个自己唯一的编号,轮廓的定位可以通过 O p e n c v Opencv OpencvconnectedComponents 方法实现,这个是执行分水岭之前的要求。

算法会根据markers传入的轮廓作为种子(也就是所谓的注水点),对图像上其他的像素点根据分水岭算法规则进行判断,并对每个像素点的区域归属进行划定,直到处理完图像上所有像素点。而区域与区域之间的分界处的值被置为“-1”,以做区分。

4 实例

目标是将下图中的硬币和背景分离:

在这里插入图片描述
在编程之前为了更好理解过程,要先介绍几个概念。

  • 背景:不感兴趣的区域,越远离目标图像中心的区域就越是背景
  • 前景:感兴趣的区域,越靠近目标图像中心就越是前景
  • 未知区域:即不确定区域,边界所在的区域

流程图:

在这里插入图片描述
代码:

import cv2 as cv
import numpy as np
import matplotlib.pyplot as pltdef test_watershed() :image = cv.imread('images/coins.jpg')image_gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)#基于直方图的二值化处理_, thresh = cv.threshold(image_gray, 0, 255, cv.THRESH_BINARY_INV + cv.THRESH_OTSU)#做开操作,是为了除去白噪声kernel = np.ones((3, 3), dtype = np.uint8)opening = cv.morphologyEx(thresh, cv.MORPH_OPEN, kernel, iterations = 2)#做膨胀操作,是为了让前景漫延到背景,让确定的背景出现sure_bg = cv.dilate(opening, kernel, iterations = 2)#为了求得确定的前景,也就是注水处使用距离的方法转化dist_transform = cv.distanceTransform(opening, cv.DIST_L2, 5)#归一化所求的距离转换,转化范围是[0, 1]cv.normalize(dist_transform, dist_transform, 0, 1.0, cv.NORM_MINMAX)#再次做二值化,得到确定的前景_, sure_fg = cv.threshold(dist_transform, 0.5 * dist_transform.max(), 255, 0)sure_fg = np.uint8(sure_fg)#得到不确定区域也就是边界所在区域,用确定的背景图减去确定的前景图unknow = cv.subtract(sure_bg, sure_fg)#给确定的注水位置进行标上标签,背景图标为0,其他的区域由1开始按顺序进行标_, markers = cv.connectedComponents(sure_fg)# cv.imshow('markers', markers.astype(np.uint8))# cv.waitKey(0)#让标签加1,这是因为在分水岭算法中,会将标签为0的区域当作边界区域(不确定区域)markers += 1#是上面所求的不确定区域标上0markers[unknow == 255] = 0# print(markers.dtype)  int32markers_copy = markers.copy()# 使用分水岭算法执行基于标记的图像分割,将图像中的对象与背景分离markers = cv.watershed(image, markers)#分水岭算法得到的边界点的像素值为-1image[markers == -1] = [0, 0, 255]images = [thresh, opening, sure_bg, dist_transform, sure_fg, unknow, markers_copy, image]titles = ['thresh', 'opening', 'sure_bg', 'dist_tranform', 'sure_fg', 'unknow', 'markers', 'image']plt.figure(figsize = (8, 6.1))for i in range(len(images)) :if i == 7 :plt.subplot(2, 4, i + 1)plt.imshow(cv.cvtColor(images[i], cv.COLOR_BGR2RGB))plt.title(titles[i])plt.axis('off')else :plt.subplot(2, 4, i + 1)plt.imshow(images[i], cmap = 'gray')plt.title(titles[i])plt.axis('off')plt.tight_layout()plt.savefig('figure.png')plt.show()if __name__ == '__main__':test_watershed()

效果:
在这里插入图片描述
从上面看来效果还是蛮好的。


http://chatgpt.dhexx.cn/article/BWRDWEEW.shtml

相关文章

MFC图像处理CImage类常用操作

原文作者&#xff1a;aircraft 原文地址&#xff1a;https://www.cnblogs.com/DOMLX/p/9598974.html MFC图像处理CImage类常用操作 CImage类头文件为#include<atlimage.h> CImage类读取图片CImage.Load("src.bmp"); CImage类保存图片CImage.Save("dst…

使用CImage进行图像处理

MFC和ATL共享的新类CImage为图像处理提供了许多相应的处理方法 CImage类 我们知道&#xff0c;Visual C的CBitmap类和静态图片控件的功能是比较弱的&#xff0c;它只能显示出在资源中的图标、位图、光标以及图元文件的内容&#xff0c;而不像VB中的Image控件可 以显示出绝大多数…

用CImage类来显示PNG、JPG等图片

系统环境&#xff1a;Windows 7 软件环境&#xff1a;Visual Studio 2008 SP1 本次目的&#xff1a;实现VC单文档、对话框程序显示图片效果 CImage 是VC.NET中定义的一种MFC/ATL共享类&#xff0c;也是ATL的一种工具类&#xff0c;它提供增强型的&#xff08;DDB和DIB&#xff…

CImage类(外部图像文件(BMP、GIF、JPEG等)

CImage类 我们知道&#xff0c;Visual C的CBitmap类和静态图片控件的功能是比较弱的&#xff0c;它只能显示出在资源中的图标、位图、光标以及图元文件的内容&#xff0c;而不像VB中的Image控件可 以显示出绝大多数的外部图像文件(BMP、GIF、JPEG等)。因此&#xff0c;想要在对…

CImage的一般使用方法和技巧

Visual C的CBitmap类的功能是比较弱的,它只能显示出在资源中的图标、位图、光标以及图元文件的内容&#xff0c;而不像VB中的Image控件可以显示出绝大多数的外部图像文件(BMP、GIF、JPEG等)。如果想要在对话框或其他窗口中显示外部图像文件则只能借助于第三方提供的控件或代码,…

图像处理(C++ CImage class)学习笔记

基础篇 A. 图像三原色及灰度值 A1. 彩色图像的三原色 图像三原色 — R&#xff1a;红色red — G&#xff1a;绿色green — B&#xff1a;蓝色blue三原色的取值范围&#xff1a;0&#xff08;无&#xff09;~255&#xff08;满&#xff09; — 红色&#xff1a;R255 G0 B0 —…

Cimage

本系列文章由zhmxy555编写&#xff0c;转载请注明出处。 http://blog.csdn.net/zhmxy555/article/details/7422922 作者&#xff1a;毛星云 邮箱&#xff1a; happylifemxyqq.com 欢迎邮件交流编程心得 我们知道&#xff0c;Visual C中的CBitmap类的功能简直太弱小了&am…

【无标题】c++ MFC图像处理CImage类常用操作代码

原文作者&#xff1a;aircraft 原文地址&#xff1a;https://www.cnblogs.com/DOMLX/p/9598974.html 我看了一下发现关于c下的CImage图像处理类 的图像处理相关的介绍真的是比较少&#xff0c;因为我要做大二的数据结构的课程设计&#xff0c;要用纯c语言去实现&#xff08;老…

C++,CImage类的建立方法(可以打开图像和保存)

建立CImage类&#xff08;以vs2015为例&#xff09; 一&#xff0c; 新建一个MFC项目&#xff1a;名字为 image3 二&#xff0c; 单个文档&#xff0c;MFC标准&#xff0c;然后完成。 三&#xff0c;打开应用程序的 stdafx.h 文件添加 CImage 类的包含文件&#xff1a; #incl…

VB.net 进程通信中FindWindow、FindWindowEX、SendMessage函数的理解

目录 一、代码背景 二、主要工具 三、函数解析 1、FindWindow&#xff1a; 2、 FindWindowEx&#xff1a; 3、SendMessage&#xff1a; 四、具体代码示例&#xff1a; 1、第一部分功能&#xff1a; A、接收端&#xff1a; B、发送端 C、运行测试 2.第二部分功能&…

C#-FindWindow的用法

C# FindWindow用法 函数功能&#xff1a;该函数获得一个顶层窗口的句柄&#xff0c;该窗口的类名和窗口名与给定的字符串相匹配。 这个函数不查找子窗口。在查找时不区分大小写。 函数型&#xff1a;HWND FindWindow&#xff08;LPCTSTR IpClassName&#xff0c;LPCTSTR IpWi…

vb.net中FindWindow方法的使用

问题描述 遇到的问题是&#xff0c;需要判断MsgBox是否已经弹出&#xff0c;如果已经弹出就不要重复弹出了。 解决方案&#xff1a; 利用FindWindow方法判断MsgBox是否已经出现 MsgBox的本质就是一个窗体&#xff0c;有标题和内容&#xff0c;可以使用FindWindow这个API函数去…

C# FindWindow的用法

找了一大堆C#怎么用FindWindowAPI函数不多说,请看步骤. 创建好WinForm窗口,如果不会创建的话,在图下面有. 项目名字和位置这个自己设置,下面那个框架,目前现在出5.0了,为了演示实例,就4.6吧. 创建完成后,把自己窗口设置一下

FindWindow ,GetWindowThreadProcessId , OpenProcess 和ReadProcessMemory

文章目录 FindWindow函数功能&#xff1a;函数声明&#xff1a;第一个参数第二个参数返回值注意&#xff1a;GetWindowThreadProcessId函数功能函数声明第一个参数&#xff1a;第二个参数&#xff1a;返回值代码实现OpenProcess函数功能&#xff1a;函数声明&#xff1a;第一个…

【CV系列】主动轮廓模型snake及其应用

DATE: 2019.5.30 前言 主动轮廓模型(Active Contour Model)&#xff0c;又被称为Snake&#xff0c;是由Andrew Blake教授提出的一种目标轮廓描述方法&#xff0c;主要应用于基于形状的目标分割。该模型的优越之处在于它对于范围广泛的一系列视觉问题给出了统一的解决方法,在最…

图像分割之Snake主动轮廓模型(Matlab代码)

示例演示 如果在中文搜索的话&#xff0c;一般会找到《数字图像处理-图像分割&#xff1a;Snake主动轮廓模型 Matlab代码及运行结果》。里面有句代码&#xff0c;千万别用&#xff0c;否则出不来效果。&#xff08;别问我怎么知道的&#xff09; % 转化为双精度型 %I im2doub…

Snake活动轮廓模型Matlab实现

1. Snake模型 人为地在图像感兴趣的区域&#xff08;ROI&#xff09;上给出初始轮廓曲线&#xff0c;最小化一个能量函数&#xff0c;使轮廓曲线在图像中运动&#xff08;变形&#xff09;&#xff0c;最终逼近该区域的边界。 设v(s)[x(s),y(s)]为活动轮廓线&#xff0c;s∈[0,…

snake主动轮廓模型

模型&#xff1a;一条可变形的参数曲线及相应的能量函数&#xff0c;以最小化能量函数为目标&#xff0c;控制参数曲线变形&#xff0c;具有最小能量的闭合曲线即是目标轮廓。 snake模型调和了上层知识和底层图像特征矛盾。 上层知识指物体形状。表示内部力。 底层图像特征是局…

Snake活动轮廓模型

1. Snake模型 人为地在图像感兴趣的区域&#xff08;ROI&#xff09;上给出初始轮廓曲线&#xff0c;最小化一个能量函数&#xff0c;使轮廓曲线在图像中运动&#xff08;变形&#xff09;&#xff0c;最终逼近该区域的边界。 设v(s)[x(s),y(s)]为活动轮廓线&#xff0c;s∈[0,…

基于边缘的主动轮廓模型——从零到一用python实现snake

从零到一实现snake算法 1、Snake算法原理2、基于曲线演化的实现方法2.1演化方程推导2.2离散化过程2.3 代码实现 3、基于水平集的实现方法4、讨论与分析源码地址[snake](https://github.com/woshimami/snake) 1、Snake算法原理 Kass等人1最早于1988年提出了主动轮廓模型&#x…