python手撕分水岭算法

article/2025/10/16 3:21:52

python手撕分水岭算法

1 分水岭算法实现

主要思路就是:

  • 利用一个优先队列与有序队列(有序队列其实可以不用)。优先队列是按像素的灰度值排列的,灰度值低的先被淹。
  • 通过统计像素的附近的点的标记种类个数来确认当前像素点的标记,并把该像素的四邻域入队。
  • 注意要使用梯度图像,这样使得各个部分蔓延的速度一样。
import cv2
import numpy as np
import matplotlib.pyplot as plt
import random
import plotly.express as px
from queue import Queue,PriorityQueue
import sysclass WATERSHED:def __init__(self,img,markers):if img.ndim!=2:raise ValueError("请输入灰度图")self.img = img# 求图像梯度幅值self.gradient()self.markers = markers.astype(np.int32)self.img_highest = np.max(self.img)self.img_lowest = np.min(self.img)self.WSHED = -1# 分水岭self.IN_QUEUE = -2#已入队,但未赋予标记# 根据梯度幅值图的灰度最大最小值创建有序队列self.ordered_queue = [[] for _ in range(self.img_highest-self.img_lowest+1)] def gradient(self):# self.img = cv2.Canny(self.img,80,150)d_x= cv2.Sobel(self.img,cv2.CV_64F,1,0,ksize=3)d_y= cv2.Sobel(self.img,cv2.CV_64F,0,1,ksize=3)value = cv2.magnitude(d_x,d_y)self.img = cv2.convertScaleAbs(value)def ordered_queue_push(self,i,j):gray_leve = self.img[i][j]self.ordered_queue[gray_leve-self.img_lowest].append([i,j])self.markers[i][j] = self.IN_QUEUEdef PriorityQueue_push(self,water_stage):# 将等于水位高度的像素点全部入优先队列for pix_coord in self.ordered_queue[water_stage-self.img_lowest]:self.wait_water_queue.put((water_stage,pix_coord))def wait_water_queue_push(self,x,y):self.wait_water_queue.put((self.img[x][y],[x,y]))self.markers[x][y] = self.IN_QUEUEdef get_label(self,pix_coord:list,water_stage):x,y = pix_coordneighbour_pix_values = {}for i in [-1,0,1]:for j in [-1,0,1]:pix_x,pix_y = x+i,y+jif pix_x<0 or pix_y<0 or pix_x >=self.img.shape[0] \or pix_y >=self.img.shape[1] :continue# 必须以四邻域搜索,八邻域会越过边界if abs(i+j)==1:if neighbour_pix_values.get(self.markers[pix_x][pix_y])!=None:neighbour_pix_values[self.markers[pix_x][pix_y]]+=1else:neighbour_pix_values[self.markers[pix_x][pix_y]]=1if self.markers[pix_x][pix_y]==0:#不在队列且无标签if self.img[pix_x][pix_y]<=water_stage:#低于水位,入优先队列self.wait_water_queue_push(pix_x,pix_y)else:#高于水位,还淹不到self.ordered_queue_push(pix_x,pix_y)pix_values = list(neighbour_pix_values)#去掉分水岭、入队标记和未标记区域值pix_values = [value for value in pix_values \if value>0 and value!=self.IN_QUEUE]if len(pix_values)==1:           self.markers[x][y]=pix_values[0]else :self.markers[x][y]=self.WSHED            def run(self):for i in range(self.img.shape[0]):for j in range(self.img.shape[1]):#已标记区域全部入队if self.markers[i][j] >0:gray_leve = self.img[i][j]self.ordered_queue[gray_leve-self.img_lowest].append([i,j])for water_stage in range(self.img_lowest,self.img_highest+1):sys.stdout.write("水位高度{}".format(water_stage))sys.stdout.flush()self.wait_water_queue = PriorityQueue()#将水位为water_stage的全部入优先队列self.PriorityQueue_push(water_stage)#每轮循环结束小于等于water_stage的像素值都得清空while self.wait_water_queue.qsize() > 0:pix_coord = self.wait_water_queue.get()[1]self.get_label(pix_coord,water_stage)# 动画plt.clf()plt.imshow(self.markers)plt.pause(0.001)return self.markers

2 获取初始标记

如果如opencv官方例程那样,用距离变换来确认标记,非常容易过分割,所以我们可以通过手动进行标记。
可以使用opencv的鼠标事件实现标记。

import cv2
import numpy as npclass draw_markers:def __init__(self,img,resize_rate:int=1):if img.ndim == 2:self.img = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR)else:self.img = imgself. resize_rate = resize_rateself.r,self.w = self.img.shape[:2]self.markers = np.zeros_like(self.img)# 放大self.img = cv2.resize(self.img,(self.w*resize_rate,self.r*resize_rate))self.markers = cv2.resize(self.markers,(self.w*resize_rate,self.r*resize_rate))self.cls_num =0#标记颜色列表self.color_list = [(255,0,0),(0,255,0),(0,0,255),(255,255,0),(255,0,255),(0,255,255)]self.drawing = Falsedef mark(self,event, x, y,flags, param):if event==cv2.EVENT_LBUTTONDOWN:self.drawing = Trueif event == cv2.EVENT_LBUTTONUP:self.drawing = Falseif self.drawing:cv2.circle(self.img,(x,y),radius=1,color=self.color_list[self.cls_num],thickness=-1)cv2.circle(self.markers,(x,y),radius=1,color=self.color_list[self.cls_num],thickness=-1)def draw(self):cv2.namedWindow('image')cv2.setMouseCallback('image',self.mark)while True:cv2.imshow('image',self.img)k = cv2.waitKey(1) & 0xFFif k==ord('c'):self.cls_num +=1print("第{}类".format(self.cls_num+1))if k==27:breakcv2.destroyAllWindows()result = cv2.cvtColor(self.markers,cv2.COLOR_BGR2GRAY)#不能用插值,会改变标签值result = result[::self.resize_rate,::self.resize_rate]cv2.imwrite('./markers.png',self.img)return result

3 主函数

  • 首先获得标记
  • 开运算
  • 分水岭

要想分割的好,要避免不同区域的连通,相同区域的分裂。锐化和高斯模糊感觉用了效果不好。

img = cv2.imread('test.png',0)
#标记图像
DM = draw_markers(img,1)
markers = DM.draw()
#开运算
kernel = np.ones((5,5),dtype=np.uint8)
img = cv2.morphologyEx(img,cv2.MORPH_OPEN,kernel,iterations=1)
# img = cv2.erode(img,kernel=np.ones((5,5)),iterations=2)
# img = cv2.GaussianBlur(img,(5,5),sigmaX=0)# #锐化
# kernel = np.array([[1, 1, 1], [1, -8, 1], [1, 1, 1]], dtype=np.float32)
# imgLaplacian = cv2.filter2D(img, cv2.CV_32F, kernel)
# sharp =np.float32(img)
# imgResult = sharp - imgLaplacian
# imgResult = np.clip(imgResult,0,255)
# imgResult = np.uint8(imgResult)
#开始分水岭算法
WSHED= watershed.WATERSHED(img,markers)
result = WSHED.run()
#将所有的分水岭转为255
result[result==-1]==255
result = result.astype(np.uint8)
imshow(result,'result.png')

4 实验结果

4.1 标记图像

在这里插入图片描述

4.2 分割结果

对比opencv官方例程,过分割现象减少。

在这里插入图片描述
在这里插入图片描述
opencv例程结果如下:
在这里插入图片描述

5 参考文献

COLOR IMAGE SEGMENTATION
Image Segmentation with Distance Transform and Watershed Algorithm
OpenCV cv::watershed 分水岭算法论文解读以及numpy实现


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

相关文章

分水岭算法java,OpenCV 学习笔记 04 深度估计与分割——GrabCut算法与分水岭算法...

1 使用普通摄像头进行深度估计 1.1 深度估计原理 这里会用到几何学中的极几何(Epipolar Geometry)&#xff0c;它属于立体视觉(stereo vision)几何学&#xff0c;立体视觉是计算机视觉的一个分支&#xff0c;它从同一物体的两张不同图像提取三维信息。 极几何的工作原理&#x…

分水岭算法-python-opencv

分水岭算法简单原理&#xff1a; 对于一个图像的灰度值&#xff0c;将图像放平&#xff0c;可以看成是类似与山谷与山顶的图像&#xff0c;灰度值小的就是山底。先找到若干个山底&#xff0c;同时加水&#xff0c;当加到一定程度时候&#xff0c;某些山顶会被淹没&#xff0c;…

分水岭算法解析[halcon]

分水岭算法 分水岭算法是根据分水岭的构成来考虑图像的分割&#xff0c; 它是—种基于拓扑理论的数学形态学的分割方法。 首先&#xff0c; 把一幅图像看作起伏的地形&#xff0c; 图像的每像素灰度值作为这个地形的高度&#xff0c; 极小值是盆地&#xff0c; 极大值为山脊。…

opencv-分水岭算法图像分割

分水岭算法图像分割 目标   本节我们将要学习   • 使用分水岭算法基于掩模的图像分割   • 函数&#xff1a;cv2.watershed()    原理   任何一副灰度图像都可以被看成拓扑平面&#xff0c;灰度值高的区域可以被看成是山峰&#xff0c;灰度值低的区域可以被看成是山…

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

1 算法简介 分水岭算法的原理很容易查到&#xff0c;但是很多文章都是直接用的opencv或matlab函数&#xff0c;看不到具体实现方法&#xff0c;这篇文章希望能对大家有点帮助。 分水岭算法就是往山谷中注水&#xff0c;把不同湖水接触的位置称作分水岭&#xff0c;这么做的结…

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…