OpenCV学习 基础图像操作(十七):泛洪与分水岭算法

原理

泛洪填充算法和分水岭算法是图像处理中的两种重要算法,主要用于区域分割,但它们的原理和应用场景有所不同,但是他们的基础思想都是基于区域迭代实现的区域之间的划分

泛洪算法

泛洪填充算法(Flood Fill)是一种经典的图像处理算法,用于确定和标记与给定点连接的区域,通常在图像填充、分割、边界检测等方面应用广泛。为了更直观地理解泛洪填充算法,我们可以通过一系列生动的图像和步骤来介绍其工作原理。

假设我们有一个二维图像,每个像素可以有不同的颜色或灰度值。泛洪填充算法的目标是从某个起始像素开始,填充所有与其相连且具有相同颜色的像素。常见的应用包括图像编辑中的填充工具(如油漆桶工具)和迷宫求解等。

算法流程

以下是泛洪填充算法的基本步骤,配合图像说明:

  1. 选择起始点和目标颜色

    1. 选择图像中的一个起始像素点(如鼠标点击的位置),记作 (x, y)。
    2. 确定要填充的目标颜色。
  2. 初始化队列

    • 将起始点 (x, y) 加入队列。
  3. 处理队列

    当队列不为空时,重复以下步骤:
    • 从队列中取出一个像素点 (cx, cy)。
    • 如果 (cx, cy) 的颜色等于目标颜色,则进行填充。
    • 将 (cx, cy) 的四个邻居(上、下、左、右)加入队列(如果这些邻居还没有被处理过且颜色等于目标颜色)。

分水岭算法

分水岭算法是一种基于形态学和拓扑学的图像分割技术。它将图像视为一个拓扑地形,通过标记图像的不同区域(例如山脉和盆地)进行分割。分水岭算法的基本思想是通过模拟雨水从山顶流向盆地的过程,确定图像中不同区域的边界。

分水岭迭代过程:

  1. 把梯度图像中的所有像素按照灰度值进行分类,并设定一个测地距离阈值。
  2. 找到灰度值最小的像素点(默认标记为灰度值最低点),让threshold从最小值开始增长,这些点为起始点。
  3. 水平面在增长的过程中,会碰到周围的邻域像素,测量这些像素到起始点(灰度值最低点)的测地距离,如果小于设定阈值,则将这些像素淹没,否则在这些像素上设置大坝,这样就对这些邻域像素进行了分类。
  4. 随着水平面越来越高,会设置更多更高的大坝,直到灰度值的最大值,所有区域都在分水岭线上相遇,这些大坝就对整个图像像素的进行了分区。

实际应用时常结合其他预处理,来实现前后景的分割:

算法流程

  1. 梯度计算: 首先计算图像的梯度,梯度可以使用 Sobel 算子或其他方法计算。梯度图像反映了图像中像素值变化的幅度。

    G(x,y)=\sqrt{(\frac{\partial I}{\partial x})^2+(\frac{\partial I}{\partial y})^2}

    其中,𝐼 是原始图像,𝐺是梯度图像。

  2. 标记区域: 对图像进行标记,将前景对象和背景标记出来。可以使用形态学操作来获取这些标记。

    • 确定前景:使用距离变换和阈值化来确定前景区域。

      D(x,y)=distance\_tranform(I)
      foreground(x,y)=\begin{cases} 1 & \text{ if } D(x,y) > 1 \\ 0 & \text{ if } othersize \end{cases}

    • 确定背景:通过膨胀操作扩展前景区域,从而确定背景区域。

      background(x,y)=dilate(foreground,kernel)

  3. 确定未知区域: 未知区域是背景和前景的差集。

    unknown=background-foreground

  4. 连接组件标记: 对前景区域进行连通组件标记,每个连通组件代表一个独立的前景对象。

    markers=connected\_components(foreground)

  5. 分水岭变换: 使用分水岭变换对梯度图像进行处理,分割图像中的不同区域。

    markers=watershed(G,markers)

    分水岭变换后,标记图像的边界区域将被标记为 -1。

API介绍

floodfill

int cv::floodFill	(	InputOutputArray 	image,   //输入图像InputOutputArray 	mask,                        //输入输出的maksPoint 	seedPoint,                               //种子点Scalar 	newVal,                                  //信的Rect * 	r    ect = , 0                           // 存储填充区域的边界Scalar 	loDiff = , Scalar()                      // 允许填充的像素值差的下届Scalar 	upDiff = , Scalar()                      // 允许填充的像素值差的上届int 	flags = 4                                // 4联通或8联通
)	
import cv2
import numpy as np
import matplotlib.pyplot as plt
def main():# 加载图像image_path = 'D:\code\src\code\lena.jpg'  # 替换为你的图像路径image = cv2.imread(image_path)if image is None:print("Error: Unable to load image.")return# 定义种子点和新颜色seed_point = (30, 30)  # 替换为你希望的种子点 (x, y)new_color = (0, 0, 255)  # 新颜色为绿色 (B, G, R)# 创建掩码,比原图多出两行两列mask = np.zeros((image.shape[0] + 2, image.shape[1] + 2), np.uint8)# 设置差值范围lo_diff = (10, 10, 10)up_diff = (10, 10, 10)image_src = image.copy()# 执行泛洪填充flags = 4  # 4-连通num, im, mask, rect = cv2.floodFill(image, mask, seed_point, new_color, lo_diff, up_diff, flags)# 显示填充后的图像plt.subplot(131),plt.imshow(image_src[...,::-1]),plt.title('Source Image'), plt.xticks([]), plt.yticks([])plt.subplot(132),plt.imshow(mask[...,::-1]),plt.title('Mask Image'), plt.xticks([]), plt.yticks([])plt.subplot(133),plt.imshow(image[...,::-1]),plt.title('Filled Image'), plt.xticks([]), plt.yticks([])plt.show()if __name__ == '__main__':main()

watermeshed

cv::watershed	(	InputArray 	image,  //输入图像
InputOutputArray 	markers             //输入出的标记
)	
//即根据传入的确信区域以及原图,经过分水岭迭代后,得到的确信区域
import cv2
import numpy as np
import matplotlib.pyplot as plt
import imageiodef plot_image(image, title, save_path):plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))plt.title(title)plt.axis('off')plt.savefig(save_path)plt.close()def save_gif(frames, filename, duration=0.5):imageio.mimsave(filename, frames, duration=duration)def watershed_segmentation(image_path):# Read the imageimage = cv2.imread(image_path)gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)# Apply thresholdingret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)# Noise removal with morphological operationskernel = np.ones((3, 3), np.uint8)opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=2)# Sure background areasure_bg = cv2.dilate(opening, kernel, iterations=3)# Finding sure foreground areadist_transform = cv2.distanceTransform(opening, cv2.DIST_L2, 5)ret, sure_fg = cv2.threshold(dist_transform, 0.7 * dist_transform.max(), 255, 0)# Finding unknown regionsure_fg = np.uint8(sure_fg)unknown = cv2.subtract(sure_bg, sure_fg)# Marker labellingret, markers = cv2.connectedComponents(sure_fg)# Add one to all labels so that sure background is not 0, but 1markers = markers + 1# Now, mark the region of unknown with zeromarkers[unknown == 255] = 0# Apply watershedmarkers = cv2.watershed(image, markers)image[markers == -1] = [255, 0, 0]  # Mark boundaries with red color# Collect frames for GIFframes = []for step in ['Original', 'Threshold', 'Morph Open', 'Sure BG', 'Sure FG', 'Unknown', 'Markers', 'Watershed']:if step == 'Original':frame = image.copy()elif step == 'Threshold':frame = cv2.cvtColor(thresh, cv2.COLOR_GRAY2BGR)elif step == 'Morph Open':frame = cv2.cvtColor(opening, cv2.COLOR_GRAY2BGR)elif step == 'Sure BG':frame = cv2.cvtColor(sure_bg, cv2.COLOR_GRAY2BGR)elif step == 'Sure FG':frame = cv2.cvtColor(sure_fg, cv2.COLOR_GRAY2BGR)elif step == 'Unknown':frame = cv2.cvtColor(unknown, cv2.COLOR_GRAY2BGR)elif step == 'Markers':frame = np.zeros_like(image)for i in range(1, ret + 1):frame[markers == i] = np.random.randint(0, 255, size=3)elif step == 'Watershed':frame = image.copy()frame_path = f"{step.lower().replace(' ', '_')}.png"plot_image(frame, step, frame_path)frames.append(imageio.imread(frame_path))return frames# Main execution
image_path = 'D:\code\src\code\R-C.png'  # Replace with your image path
frames = watershed_segmentation(image_path)
save_gif(frames, 'watershed.gif', duration=1000)

参考链接

OpenCV(26)图像分割 -- 距离变换与分水岭算法(硬币检测、扑克牌检测、车道检测)_分水岭算法分割咖啡豆-CSDN博客

图像处理之漫水填充算法(flood fill algorithm)-腾讯云开发者社区-腾讯云 (tencent.com)

【OpenCV(C++)】分水岭算法_opencv分水岭c++-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/341159.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

中电金信:从规划到落地,中电金信全程陪伴式服务助力泛金融数字化转型

在当前的全球经济和金融发展格局中,金融行业正经历着一场以数字化为核心的快速转型。中国银行业和保险业已经成功探索出一条数字化转型的路径,并积累了丰富的实践经验。然而,泛金融领域则仍处于数字化转型的初期阶段,其转型能力因…

【案例实战】 基于OpenCV实现鹿茸面积计算

学习《人工智能应用软件开发》,学会所有OpenCV技能就这么简单! 做真正的OpenCV开发者,从入门到入职,一步到位! 有人在我得B站答疑群里发了下面的图: 问:如何计算鹿茸最外圈蜡皮面积占整个鹿茸…

AI 入门指南二 :AI提示词(Prompt)

一,提示词的定义 提示词在中文中意为“触发”,在自然语言处理(NLP)的领域,它更接近于一个“心领神会”的概念,而非具有明确定义的术语。 简而言之,提示词是用户对大型语言模型的输入&#xff0…

Centos 7部署NTP

介绍 NTP是Network Time Protocol(网络时间协议)的简称,它是用来通过互联网或局域网将计算机时钟同步到世界协调时间(UTC)的协议。 安装 # yum安装 yum install -y ntp# 离线安装 #下载地址:https://mir…

全球首款AR电脑上线,可投影100英寸屏幕

近日,Sightful公司推出了一款名为Spacetop G1的革命性笔记本电脑,将AR技术与传统笔记本电脑巧妙融合,打造出令人惊叹的全新办公体验。 全球首款AR电脑上线,可投影100英寸屏幕 不同于传统笔记本电脑依赖物理屏幕显示内容&#xff0…

新手如何正确使用代理IP,一篇文章学会,包含实战案例

前言 一、代理IP1.1 什么是代理IP?1.2 代理ip分类1.3 代理IP的作用和优势 二、更换代理IP的方法2.1 重启路由器或光猫2.2 用拨号 vps 重拨更换动态IP代理。2.3 使用浏览器更换IP 三、IPIDEA代理的优势四、提取代理IP4.1 提取步骤4.2 浏览器使用代理IP 五、使用代理I…

c#基础()

学习目标 了解:嵌套类,匿名类,对象初始化器 重点:类的定义以及对象,构造方法,this和static关键字 掌握:面向对象的概念,访问修饰符,垃圾回收 面向对象 面向对象的概…

微信小程序毕业设计-在线订餐系统项目开发实战(附源码+论文)

大家好!我是程序猿老A,感谢您阅读本文,欢迎一键三连哦。 💞当前专栏:微信小程序毕业设计 精彩专栏推荐👇🏻👇🏻👇🏻 🎀 Python毕业设计…

Flink系列一:flink光速入门 (^_^)

引入 spark和flink的区别:在上一个spark专栏中我们了解了spark对数据的处理方式,在 Spark 生态体系中,对于批处理和流处理采用了不同的技术框架,批处理由 Spark-core,SparkSQL 实现,流处理由 Spark Streaming 实现&am…

期刊的分类与级别

在学术界,期刊的分类与级别构成了一个评价学术成果和学者贡献的重要标准,同时也是学术出版与学术交流的基础。然而,对于初涉学者来说,理解期刊的分类与级别可能并不直观。本文旨在提供一个系统性的解释,并阐述为何期刊…

云南区块链商户平台发票助手成品

目录 1 概述2 功能对比3 项目演示图4 核心逻辑4.1智能赋码4.2 解密方法4.3 登录与检测4.4 发票金额大写转换4.5 检查登录是否失效4.6 验证码识别5 演示效果6 项目部署6.1 Web站点部署6.1.1 环境6.1.2 前端6.1.3 后端6.2 Docker部署6.2.1 构建镜像6.2.2 创建容器6.3.3 访问项目域…

基于PHP+MySQL开发的一套游泳馆预约报名小程序开发源码模板

最近新开发了一套游泳馆线上预约报名小程序,其主要功能有预约功能,报名功能,支付功能,个人中心,订单管理,商品管理等等。 游泳馆预约报名小程序系统-运行环境 开发语言:PHP 数据库:M…

54.WEB渗透测试-信息收集- 端口、目录扫描、源码泄露(2)

免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动! 内容参考于: 易锦网校会员专享课 上一个内容:53.WEB渗透测试-信息收集-端口、目录扫描、源码泄露(1) 关于源码…

Postman 连接数据库 利用node+xmysql

1、准备nodejs环境 如果没有安装,在网上找教程,安装好后,在控制台输入命令查看版本,如下就成功了 2、安装xmysql 在控制台输入 npm install -g xmysql 3、连接目标数据库 帮助如下: 示例: 目标数据库…

【Redis数据库百万字详解】数据持久化

文章目录 一、持久化1.1、什么是持久化1.2、持久化方式1.3、RDB优缺点1.4、AOF优缺点 二、RDB持久化触发机制2.1、手动触发2.2、自动触发 三、RDB持久化配置3.1、配置文件3.2、配置查询/设置3.3、禁用持久化3.4、RDB文件恢复 四、RDB持久化案例4.1、手动持久化4.2、自动持久化案…

补充一下关于大众点评如何获取cookie的问题

第一步:先打开网站https://www.dianping.com/,然后再用手机号登录,在选择自己要爬的城市 第二步:打开按f12或者右键鼠标选择最下面的检查就打开这个页面了,如果是英文页面就选择network,如果是中文就选择网络 第三步&a…

2024精选热门骨传导耳机推荐,你不会还不挑选吧?

骨传导耳机作为最近两年来才兴起的耳机品类,在街头的出现频率并不是很高,很多人对骨传导耳机不够了解甚至没听说过。骨传导耳机不入耳的设计,安全性、舒适性和稳定性都更高,既然有这么多的优势,那就为大家挑选几款高性…

HTML静态网页成品作业(HTML+CSS)—— 美食湘菜介绍网页(5个页面)

🎉不定期分享源码,关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 🏷️本套采用HTMLCSS,未使用Javacsript代码,共有5个页面。 二、作品演示 三、代…

层出不穷的大模型产品,你怎么选?

一:简介 关于大模型AIGC产品的选择与发展趋势,目前许多互联网公司都在不断投入资源和精力开发基于大规模模型的人工智能产品。这些产品通常能够处理更复杂的任务并提供更高质量的服务,如智能问答、自然语言处理、图像识别等。在产品选择上&am…

python11 序列的相关操作

枚举遍历 序列的相关操作 text "hello,python" # in 判断字符是否在序列中,存在返回true,否则返回false print(p是否存在:,(p in text)) print(a是否存在:,(a in text)) # not in 判断字符不在序列中,不存在返回true,否则返回false print(p不…