Python OpenCV分水岭算法实现图像分割详解

江屿
江屿
发布时间:2026-10-13| 7人浏览过
原创

本文详解OpenCV中cv2.watershed()函数的原理与应用。通过灰度拓扑模型解释算法机制,并介绍基于标记的分水岭算法以减少过度分割。结合Otsu二值化、形态学操作及距离变换,演示如何确定前景、背景及不确定区域,最终实现粘连对象的精准分割。

本文分享Python OpenCV中分水岭算法的具体应用示例,供读者参考学习。

目标

  1. 利用分水岭算法对带有标记的图像执行分割操作。
  2. 调用cv2.watershed()函数。

原理:

可将灰度图像视为拓扑平面,其中灰度值较高的区域被视为山峰,灰度值较低的区域被视为山谷。向各个山谷中注入不同颜色的水,随着水位上升,不同山谷的水体可能汇合。为避免水体混合,需在汇合处构建堤坝。持续灌水并不断建立堤坝,直至所有山峰被淹没。最终形成的堤坝结构即为图像的分割边界。

该方法常因图像噪声等因素导致过度分割。为改善此问题,OpenCV采用基于标记的分水岭算法,需预先指定哪些山谷汇合点有效、哪些无效。这是一种交互式图像分割方法,需对已知对象赋予不同标记。若某区域确认为前景或目标,则用特定颜色或灰度值标记;若确认为背景,则用另一颜色标记;其余不确定区域标记为0。随后运行分水岭算法,每次灌水过程会更新标签,当两种不同颜色的标签相遇时即构建堤坝,直至所有山峰被覆盖。最终得到的边界对象值为-1。

代码:

针对相互接触的对象进行分割处理。

应用Otsu's二值化后的结果如图所示。

为去除图像中的白色噪声,可采用形态学运算。使用闭运算可消除对象内部的空洞。

靠近对象中心的区域判定为前景,远离对象的区域判定为背景,而中间地带则视为不确定区域。

首先提取硬币区域,通过腐蚀操作去除边缘,剩余部分即为硬币主体。此方法在硬币互不接触时有效,但因硬币相互接触,需采用另一种有效策略:结合距离变换与合适的阈值处理。

接着寻找不确定是否属于硬币的区域。此处需进行膨胀操作,使对象边界延伸至背景区域。由于边界区域已被处理,从而能够明确区分前景与背景。

剩余无法明确区分的区域交由分水岭算法处理,这些区域通常位于前景与背景的交界处。通过从确认为背景的区域中减去确认为前景的区域,即可得到边界区域。

(前景和背景)

(上图展示了直接运行作者代码后的结果,成功提取前景。为演示不确定区域,调整了前景距离变换的计算参数,导致中间出现不确定区域)。

此处使用cv2.distanceTransform函数。

该函数用于计算二值图像中所有像素到最近值为0像素的近似距离。

参数说明如下:

cv2.distanceTransform(src, distanceType, maskSize[, dst]) → dst

#src为输入的二值图像。distanceType为计算距离的方式,可以是如下值
DIST_USER = ⑴, //!< User defined distance
DIST_L1  = 1, //!< distance = |x1-x2| + |y1-y2|
DIST_L2  = 2, //!< the simple euclidean distance
DIST_C  = 3, //!< distance = max(|x1-x2|,|y1-y2|)
DIST_L12  = 4, //!< L1-L2 metric: distance = 2(sqrt(1+x*x/2) - 1))
DIST_FAIR = 5, //!< distance = c^2(|x|/c-log(1+|x|/c)), c = 1.3998
DIST_WELSCH = 6, //!< distance = c^2/2(1-exp(-(x/c)^2)), c = 2.9846
DIST_HUBER = 7 //!< distance = |x|<c ? x^2/2 : c(|x|-c/2), c=1.345


#maskSize是蒙板尺寸,只有0,3,5
DIST_MASK_3  = 3, //!< mask=3
DIST_MASK_5  = 5, //!< mask=5
DIST_MASK_PRECISE = 0 //!< mask=0
import numpy as np
import cv2
from matplotlib import pyplot as plt
img = cv2.imread('21.jpg')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)

kernel = np.ones((3,3),np.uint8)
opening = cv2.morphologyEx(thresh,cv2.MORPH_OPEN,kernel, iterations = 2)
# sure background area
sure_bg = cv2.dilate(opening,kernel,iterations=3)#膨胀
# Finding sure foreground area

dist_transform = cv2.distanceTransform(opening,1,5)
ret, sure_fg = cv2.threshold(dist_transform,0.2*dist_transform.max(),255,0)#参数改小了,出现不确定区域
# Finding unknown region
sure_fg = np.uint8(sure_fg)
unknown = cv2.subtract(sure_bg,sure_fg)#减去前景

cv2.imshow('p',sure_fg)
cv2.waitKey(0)

现已确定哪些背景区域属于硬币,可创建标签数组(与原图像尺寸相同,数据类型为int32)。

对于已确定分类的区域(背景与前景),使用整数进行标记;不确定区域则标记为0。可利用cv2.connectedComponents()函数实现此功能,该函数将背景标记为0,其他区域标记为从1开始的正整数。

若将背景标记为0,分水岭算法会将其视为无效区域。因此需使用不同的整数进行标记,而对不确定区域,函数仍标记为0。

结果采用JET颜色地图显示:深蓝色表示未知区域,硬币区域使用不同颜色区分,其余部分用浅蓝色表示。

执行分水岭算法。

分割效果良好。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。