分水岭算法的理解和应用

article/2025/10/16 5:29:16

分水岭算法

  • 主要思想
    • 图像的灰度空间很像地球表面的整个地理结构,每个像素的灰度值代表高度。分水岭就是灰度值较大的像素连成的线。二值化阈值可以理解为水平面,比灰度二值化阈值小的像素区域会被淹没。
    • 随着水位线的升高,被淹没的区域越来越多,通过“修建大坝”防止两个区域的水汇集。最终整张图像分成两个部分:一是水坝线(分水岭线像素集),二是水淹区域(像素集)。

1 预备知识

1.1 测地距离

  • 首先,当根据经纬度计算地球上两点间的距离时,使用的是球面距离,而非欧氏距离
  • 相似的,分水岭算法中用到的测地距离,是三维的,即[x,y,h]
  • 测地距离计算方法为:两点间沿着三维表面的曲面走的最短路径

1.2 固定图像二值化:cv2.threshold函数解析

  • retval, dst = cv2.threshold(src, thresh, maxval, type[, dst])
    • retval, dst分别表示二值化灰度阈值和二值化后的图像数据
    • scr:图像数据
    • thresh:自定义阈值
    • maxval:最大值,像素超过了阈值,所赋予的值
    • type:可选择的算法类型
  • 例子
    • 超过阈值的设为maxval,其他为0:ret,thresh1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY)
    • 超过阈值的设为0,其他未maxval:ret,thresh2 = cv2.threshold(img,127,255,cv2.THRESH_BINARY_INV)
    • 大于阈值部分设为阈值,其他不变:ret,thresh3 = cv2.threshold(img,127,255,cv2.THRESH_TRUNC)
    • 低于阈值的设置为0,其他保持不变:ret,thresh4 = cv2.threshold(img,127,255,cv2.THRESH_TOZERO)
    • 低于阈值的设置为maxval,其他保持不变ret,thresh5 = cv2.threshold(img,127,255,cv2.THRESH_TOZERO_INV)

1.3 自适应二值化:cv2.adaptiveThreshold

  • dst = cv2.adaptiveThreshold(src, maxval, thresh_type, type, Block Size, C)
    • thresh_type:
      • cv2.ADAPTIVE_THRESH_MEAN_C:计算均值时每个像素的权值是相等的
      • cv2.ADAPTIVE_THRESH_GAUSSIAN_C:计算均值时每个像素的权值根据其到中心点的距离通过高斯方程得到
    • type:计算均值后,即生成阈值,而后采取固定图像二值化方法
    • block size:图片分块的大小
    • C:阈值计算中的所加减的常数项

1.4 图像形态学

  • 原理介绍:需要在二值化后的图像中进行。相当于对图像进行池化操作,膨胀相当于最大池,图像的高亮区域或白色部分进行扩张,其运行结果图比原图的高亮区域更大;腐蚀相当于最小池,将图像中的高亮区域或白色部分进行缩减细化,其运行结果图比原图的高亮区域更小。
  • 卷积核锚点(中心点)值被最亮/暗像素值替换
  1. 图像膨胀
  • dst = cv2.dilate(src, kernel, iterations)
    • kernel:卷积核,n*n维矩阵
    • interations:迭代次数
    • 作用:试图找到连通分支(具有相似颜色或强度的像素点的大块互相分离的区域)。因为多数情况下,一个大区域可能被噪声、阴影等分割成多个部分
  1. 图像腐蚀
  • dst = cv2.erode(src, kernel, iterations)
    • 作用:消除图像中“斑点”噪声,且能确保图像内较大区域依然存在
  1. 创建核的方法
  • 使用numpy构建结构化元素,都是正方形的核。
    • np.ones((5,5), np.unit8)
  • 如果要构建圆形或者椭圆形的核,可以使用cv2.getStructuringElement()函数
    • kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))
    • MORPH_RECT:矩形
    • MORPH_CROSS:交叉十字
    • MORPH_ELLIPSE:椭圆
    • (n,n):代表卷积核尺寸
  1. 更加通用的形态学
  • opening = cv2.morphologyEx(data, type, kernel, iterations)

  • 开运算

    • 先腐蚀再膨胀:向上的孤立点被消除,去除小的明亮区域,并且剩余的明亮区域被隔绝,但大小不变
    • type:cv2.MORPH_OPEN
  • 闭运算

    • 先膨胀后腐蚀:消除低亮度值的孤立点,亮的区域合在一起,但他们的基本大小不变
    • type:cv2.MORPH_CLOSE
  • 形态学梯度

    • 膨胀后的图像-腐蚀后的图像
    • 可以突出边缘
    • type:cv2.MORPH_GRADIENT
  • 礼帽和黑帽

    • 礼帽:原图-开运算后的图像
      • 作用:突出比原图中周围区域更明亮的部分,与核的大小相关
    • 黑帽:原图-闭运算后的图像
      • 作用:突出比原图中周围区域更暗的部分,与核的大小相关

2 算法流程

  • 把梯度图像中的所有像素按照灰度值分类,并设定一个测地距离阈值
  • 找到灰度值最小的像素点(默认标记为灰度值最低点),让threshold从最小值开始增长,这些点为起始点。
  • 水平面在增长的过程中,会碰到周围的邻域像素,测量这些像素到起始点(灰度值最低点)的测地距离,如果小于设定阈值,则将这些像素淹没,否则在这些像素上设置大坝,这样就对这些邻域像素进行了分类。
  • 思考:这个阈值应该如何设定?若设定偏低,则会出现较多杂乱的边缘线,若设定偏高,则往往“防洪”效果不好。

3 opencv实例操作

3.1 导入相关库并读取数据

import numpy as np
import matplotlib.pyplot as plt
import cv2
import os
import pandas as pdpath = r''
origin = cv2.imread(path)

在这里插入图片描述

3.2 图像预处理

(一) 转化为灰度图片,并进行二值化处理
gray = cv2.cvtColor(origin, cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY|cv2.THRESH_OTSU)

在这里插入图片描述

(二) 去除杂点:三次膨胀+两次腐蚀+一次膨胀
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2,2))
opening1 = cv2.dilate(binary, kernel, iterations = 3)
opening2 = cv2.erode(opening1, kernel, iterations = 2)
opening3 = cv2.dilate(opening2, kernel, iterations=1)

在这里插入图片描述

3.3 确定前景和背景和未知区域

  • 下面是我自己的理解啦
  • 前景:确定属于目标的部分
  • 背景:一定包含全部目标的部分,背景包含前景
  • 未知区域:背景-前景,因此包含边缘线
(一) 确定前景
sure_bg = cv2.dilate(opening3, kernel, iterations = 3) # 通过膨胀获取背景区域

在这里插入图片描述

(二)确定背景
dist = cv2.distanceTransform(opening3, cv2.DIST_L2, 5) # 通过距离变换获取前景区域
ret, sure_fg = cv2.threshold(dist, dist.max()*0.45, 255, cv2.THRESH_BINARY) # 0.45根据实际情况来调

在这里插入图片描述

(三)确定未知区域
surface_fg = np.uint8(sure_fg)  # 背景空间为整型空间,前景为浮点型空间,这里做一下统一
unknown = cv2.subtract(sure_bg, surface_fg)
plt.imshow(unknown, cmap = plt.cm.gray)

在这里插入图片描述

3.4 创建markers并实现watershed

  • cv2.watershed函数需要将包含边缘的未知区域设置为0
ret, markers = cv2.connectedComponents(surface_fg) # 所有的前景区域非0
markers = markers + 1 # 此时所有的区域非零,其中包含边缘的未知区域和非背景区域为1
markers[unknown == 255] = 0  # 令包含边缘的位置区域为0# 注:watershed会将找到的栅栏在markers中设置为-1markers = cv2.watershed(origin, markers = markers) # 获取栅栏
origin[markers == -1] = [0,0,255]  # 根据栅栏,我们对原图像进行操作,对栅栏区域设置为红色

在这里插入图片描述

  • 分水岭算法的简单应用就先到这里啦,想要得到更精细的“水坝线”,还需要自己多多调试
  • 欢迎邮件交流:hu.yf@outlook.com

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

相关文章

分水岭算法及其实现

1 - 算法描述 1.1 分水岭算法的原理   分水岭的概念是以三维方式来形象化一幅图像为基础的:两个空间坐标再加上强度。在这种“地形学”解释中,考虑三种类型的点:(a)局部最小值点,该点对应一个…

传统图像分割——分水岭算法(watershed)

传统图像分割——分水岭算法(watershed) 文章目录 传统图像分割——分水岭算法(watershed)前言一、什么是分水岭算法?二、经典的分水岭求解算法1.定义2.算法流程 总结 前言 本篇文章主要梳理分水岭算法的原理&#xf…

图像分割 - 分水岭算法

目录 1. 介绍 2. 分水岭算法的实现 距离变换 连接连通分量 3. 代码 1. 介绍 图像是由x,y表示的,如果将灰度值也考虑进去的话,那么一幅图像需要一个三维的空间去表示。 这样就可以把x,y轴比作大地,将灰度值的z轴…

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

文章目录 1 原理2 算法改进3 API4 实例 1 原理 分水岭分割方法,是一种基于拓扑理论的数学形态学的分割方法,其基本思想是把图像看作是测地学上的拓扑地貌,图像中每一点像素的灰度值表示该点的海拔高度,每一个局部极小值及其影响区…

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…