分水岭算法的python实现及解析

article/2025/10/16 3:25:33

1 算法简介

分水岭算法的原理很容易查到,但是很多文章都是直接用的opencv或matlab函数,看不到具体实现方法,这篇文章希望能对大家有点帮助。

分水岭算法就是往山谷中注水,把不同湖水接触的位置称作分水岭,这么做的结果有两个:
1.得到整个区域的分界线
2.把整个区域编号

这是一种很容易过分割的算法,需要一些预处理手段和一些优化,本文只解析基本原理
(下图的注水过程是一种基于人为标记的方法)

注水过程

2 代码步骤说明

分水岭算法的步骤如下:

1.将图像的所有像素按像素值从小到大排序,这里可以利用直方图将像素信息塞入数组

2.开始按灰度级从小到大顺序遍历所有像素,先将该灰度级的全部像素标记为待计算点,若点的邻域内有已存在的水池,则放入一个队列(先将水池边缘像素入队)

3.开始遍历队列,直到队列为空

3.1 若当前像素周围没有已经编号过的像素,则是新的水池
给与一个新的编号,并将邻域内的同灰度级像素加入队列
一直遍历队列到队列为空,即将这个新水池底部填满3.2 若像素周围有已经计算过的像素则看情况:
3.2.1 如果周围有2个不同的水池编号,则是分水岭
3.2.2 如果周围只有1个水池编号,则该点也是这个编号
3.2.3 周围有同层(待计算点),入队

4.开始计算下一个灰度级

所以这个算法就是从每个水池的边界开始一圈一圈,一个灰度级一个灰度级地向外蔓延的过程

3 python实现

import cv2
import numpy as np
from queue import Queue#队列maxhigh = 255 #水位
mask = -2       #用于标记每次涨水时,将会被水淹没的像素
watershed = 0   #用于标记分水岭边缘
#fixmark = np.array([-1,-1])#用于隔开队列的每个部分def checkedge(inputidx,w,h):if inputidx[0] >= h:return -1if inputidx[1] >= w:return -1if inputidx[0] < 0:return -1if inputidx[1] < 0:return -1return 0#def mark_end_que(que):# que.put(fixmark) def water(inputimg, size):imsize = size[0]*size[1]w = size[1]h = size[0]pixarray = np.zeros([imsize,2])#用于储存所有像素,以及坐标labelout = np.zeros(size)-1#distance = np.zeros(size)putquemask = np.zeros(size)labelsize = np.zeros(imsize)cnt = np.zeros(257)#累积直方图currenLabel = 0#湖区标号que = Queue(maxsize=0)#创建队列neighborbias4 = np.array([[0,-1],[-1,0],[0,1],[1,0]])#邻域偏移#neighborbias8 = np.array([[0,-1],[-1,-1],[-1,0],[-1,1],[0,1],[1,1],[1,0],[1,-1]])#邻域偏移#计算直方图,知道每个灰度级的位置hist = cv2.calcHist([inputimg],[0],None,[256],[0,256])#累计直方图的点就是每个灰度级在pixarray中的起始位置,但是要去除空的点hist[0] = hist[0] - 1#由于累积直方图是作为下标使用,因此最终求和结果不可等于imsizehistsum = 0for i in range(0,256):histsum = histsum + hist[i]cnt[i+1] = histsumcnt = cnt.astype(np.int32)cntidx = cnt[1:257].copy()#遍历图片记录全部像素,根据累积直方图排列像素顺序,到时候按顺序从低到高一个个涨水for y in range(0,h):for x in range(0,w):pix = inputimg[y,x]pixarray[(cntidx[pix]),:] = y,xcntidx[pix] = cntidx[pix] - 1#准备完成,开始涨水for nowgraylevel in range(0,maxhigh+1):print(nowgraylevel)#标记当前层灰度for idx in range(cnt[nowgraylevel],cnt[nowgraylevel+1]+1):nowpixaxis = pixarray[idx].astype(np.int32)labelout[nowpixaxis[0],nowpixaxis[1]] = mask#把在水池边缘的当前灰度级入队for nei in range(0,4):neighboraxis  = nowpixaxis + neighborbias4[nei]if checkedge(neighboraxis,w,h) < 0:continueif labelout[neighboraxis[0],neighboraxis[1]] >= 0:#周围有已经计算过的putquemask[neighboraxis[0],neighboraxis[1]] > 0#标记已经入队过了que.put(nowpixaxis)#入队break#开始遍历队列while(True):if que.empty():breaknowpixaxis = que.get()#蔓延过程#1 周围有1个label,加入其中#2 周围两个不同label,分水岭#3 周围无label,新水池#4 周围有同层,入队蔓延for nei in range(0,4):neighboraxis  = nowpixaxis + neighborbias4[nei]if checkedge(neighboraxis,w,h) < 0:continuelabelnow = labelout[nowpixaxis[0],nowpixaxis[1]]labelneighbor = int(labelout[neighboraxis[0],neighboraxis[1]])if labelnow == -2 and labelneighbor > 0:labelout[nowpixaxis[0],nowpixaxis[1]] = labelneighborlabelsize[labelneighbor] = labelsize[labelneighbor] + 1elif labelnow > 0 and labelneighbor > 0 and labelnow != labelneighbor:labelout[nowpixaxis[0],nowpixaxis[1]] = 0if labelneighbor == -2 and putquemask[neighboraxis[0],neighboraxis[1]] == 0:que.put(neighboraxis)putquemask[neighboraxis[0],neighboraxis[1]] = 1#标记这个像素已经入队了不要重复使用#找到了新的水坑(邻域没有水)for idx in range(cnt[nowgraylevel],cnt[nowgraylevel+1]+1):nowpixaxis = pixarray[idx,:].astype(np.int32)if(labelout[nowpixaxis[0],nowpixaxis[1]] == -2):#经过之前的赋值,仍然没有被标记的是新水池#print("new pool %d",)currenLabel = currenLabel + 1#配置序号labelout[nowpixaxis[0],nowpixaxis[1]] = currenLabelque.put(nowpixaxis)#将新坑底所有同灰度级像素都填上水while not que.empty():nowpixaxis = que.get()for nei in range(0,4):neighboraxis = nowpixaxis + neighborbias4[nei]#防越界if checkedge(neighboraxis,w,h) < 0:continueif  putquemask[neighboraxis[0],neighboraxis[1]] == 0 and labelout[neighboraxis[0],neighboraxis[1]] == -2:labelout[neighboraxis[0],neighboraxis[1]] = currenLabelque.put(neighboraxis)putquemask[neighboraxis[0],neighboraxis[1]] = 1labelsize[currenLabel] = labelsize[currenLabel] + 1return labelout    image = cv2.imread('F:/tf_learn--------/impo/coin3.png')  #F:/tf_learn--------/impixiv/1606.jpg #F:/tf_learn--------/impo
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY, cv2.CV_16S)size = gray.shape  # h w c#二值化
ret,gray = cv2.threshold(gray,40,255,cv2.THRESH_BINARY)
cv2.imshow("ret",gray)
cv2.waitKey(0)
#膨胀
kernel = np.ones((5,5),np.uint8)
gray = cv2.dilate(gray,kernel)#腐蚀
gray = cv2.erode(gray,kernel)#边缘检测
#edgeimage = np.uint8(np.abs(cv2.Sobel(gray,cv2.CV_16S, 1, 1, ksize=3)))#这个sobel不太方便
edgeimage = cv2.Canny(gray, 10, 100) cv2.imshow("gray",edgeimage)
cv2.waitKey(0)label = water(edgeimage,size)
label = cv2.resize(label,(size[1],size[0]))#标记
b=image[:,:,0]
g=image[:,:,1]
r=image[:,:,2]
r[label==0] = 255
g[label==2] = 255
b[label==3] = 255
b[label==4] = 125
g[label==4] = 125largeimage = cv2.resize(image,(size[1],size[0]),cv2.INTER_LINEAR)
cv2.imshow("lab",largeimage)
cv2.waitKey(0)

4 效果

在这里插入图片描述
在这里插入图片描述


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

相关文章

opencv28:分水岭算法的图像分割

目标 在本章中&#xff0c;将学习 使用分水岭算法实现基于标记的图像分割函数&#xff1a;cv2.watershed() 理论 任何灰度图像都可以看作是一个地形表面&#xff0c;其中高强度的像素表示山峰&#xff0c;低强度表示山谷。可以用不同颜色的水(标签)填充每个孤立的山谷(局部…

OpenCV-分水岭算法

文章目录 分水岭算法cv2.watershed示例 分水岭算法 任何灰度图像都可以看作是一个地形表面&#xff0c;其中高强度表示山峰&#xff0c;低强度表示山谷。你开始用不同颜色的水(标签)填充每个孤立的山谷(局部最小值)。随着水位的上升&#xff0c;根据附近的山峰(坡度)&#xff…

【OpenCV】- 分水岭算法

文章目录 什么是图像分割分水岭算法1、实现分水岭算法&#xff1a;watershed()函数2、处理流程&#xff08;视频&#xff09;3、示例程序&#xff08;书中&#xff09; 什么是图像分割 将图像中像素根据一定的规则分为若干个cluster集合&#xff0c;每个集合包含一类对象 如下…

OpenCV分水岭算法详解

原理分析 分水岭算法主要用于图像分段&#xff0c;通常是把一副彩色图像灰度化&#xff0c;然后再求梯度图&#xff0c;最后在梯度图的基础上进行分水岭算法&#xff0c;求得分段图像的边缘线。 下面左边的灰度图&#xff0c;可以描述为右边的地形图&#xff0c;地形的高度是由…

分水岭算法 matlab实现

背景 做图像分割的时候用到了&#xff0c;就学习了一下 大概思想 把图像中的像素大小理解成山地的海拔&#xff0c;向山地灌水&#xff0c;海拔低的地方会积水&#xff0c;这些地方称之为谷底。随着水位上升&#xff0c;不同谷底的水会相遇&#xff0c;相遇的地方就是分水岭。…

分水岭算法c语言,Opencv分水岭算法学习

分水岭算法可以将图像中的边缘转化成“山脉”&#xff0c;将均匀区域转化为“山谷”&#xff0c;这样有助于分割目标。 分水岭算法是一种基于拓扑理论的数学形态学的分割方法&#xff0c;其基本思想是把图像看作是测地学上的拓扑地貌&#xff0c;图像中的每一点像素的灰度值表示…

分水岭算法

引言&#xff1a;它是基于拓扑理论的形态学处理方法。将一张图像假想成为一张地貌特征图。 原理理解&#xff1a;灰度图被看作拓扑平面&#xff0c;灰度高看成山峰&#xff0c;灰度低看成山谷。从山谷开始注水&#xff0c;随着水位升高水流会相遇汇合。为了防止汇合&#xff0…

Opencv分水岭算法——watershed自动图像分割用法

分水岭算法是一种图像区域分割法,在分割的过程中,它会把跟临近像素间的相似性作为重要的参考依据,从而将在空间位置上相近并且灰度值相近的像素点互相连接起来构成一个封闭的轮廓,封闭性是分水岭算法的一个重要特征。 其他图像分割方法,如阈值,边缘检测等都不会考虑像素在…

目标分割算法之分水岭算法

分水岭算法 1.经典算法原理及实现 传统的目标分割算法主要分为两种 1.基于像素相似性&#xff1a;阈值分割、k-means分割 2.基于像素邻域关系&#xff1a;区域生长、分水岭、基于标记分水岭 分水岭算法原理 如图中展现了凹凸不平的地貌&#xff0c;视觉上明显的位置有盆地及…

分水岭算法的理解和应用

分水岭算法 主要思想 图像的灰度空间很像地球表面的整个地理结构&#xff0c;每个像素的灰度值代表高度。分水岭就是灰度值较大的像素连成的线。二值化阈值可以理解为水平面&#xff0c;比灰度二值化阈值小的像素区域会被淹没。随着水位线的升高&#xff0c;被淹没的区域越来越…

分水岭算法及其实现

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

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

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

图像分割 - 分水岭算法

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

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

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

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 —…