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.mzph.cn/bicheng/20527.shtml

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

相关文章

修改element-ui el-radio颜色

修改element-ui el-radio颜色 需求效果图代码实现 小结 需求 撤销扣分是绿色&#xff0c;驳回是红色 效果图 代码实现 dom <el-table-columnlabel"操作"width"200px"><template v-slot"scope"><el-radio-group v-model"s…

Vue插槽与作用域插槽

title: Vue插槽与作用域插槽 date: 2024/6/1 下午9:07:52 updated: 2024/6/1 下午9:07:52 categories: 前端开发 tags:VueSlotScopeSlot组件通信Vue2/3插槽作用域API动态插槽插槽优化 第1章&#xff1a;插槽的概念与原理 插槽的定义 在Vue.js中&#xff0c;插槽&#xff08;…

如何用FPGA实现SINC滤波

目录 简介: 技术说明: 代码如下: 简介: sinc(音同“sink”)滤波器是由sinc函数构造的滤波器。sinc函数的定义可以参考抽样信号Sa的定义,这里只需知道矩形脉冲和sinc函数是一个变换对。当矩形脉冲的频谱没有混叠时,它就是sin(x)/x,一个sinc函数,对于连续信号,矩形脉…

【记录43】el-table @selection-change 数据回显、条件约束、历史回显清除

场景 在其他地方设置好人员&#xff0c;到对应的页面直接在表格中复选设置好的人员。解决方案用到selection-change方法 <el-button click"EchoClick()">回显设置好的人</el-button> <el-table ref"choeck" :data"TableData" s…

c++(七)

c&#xff08;七&#xff09; 内联函数内联函数的特点为什么要有内联函数内联函数是如何工作的呢 类型转换异常处理智能指针单例模式懒汉模式饿汉模式 VS中数据库的相关配置 内联函数 修饰类的成员函数&#xff0c;关键字&#xff1a;inline inline 返回值类型 函数名(参数列…

vue-el-steps 使用2[代码示例]

效果图 代码 element代码 <template> <div class"app-container"> <el-form :model"queryForm" size"small" :inline"true"> <el-form-item label"内容状态"> <el-button-group> <el-bu…

Docker 容器中运行Certbot获取和管理 SSL 证书

如果你在 Docker 容器中运行 Nginx 并希望使用 Certbot 获取和管理 SSL 证书&#xff0c;可以使用 Certbot 的官方 Docker 镜像来完成这项工作。以下是使用 Docker 和 Certbot 获取 SSL 证书并配置 Nginx 的详细步骤&#xff1a; 1. 拉取 Certbot Docker 镜像 首先&#xff0…

window下C语言程序报错

Thread 1 received signal SIGSEGV, Segmentation fault. ___chkstk_ms () at ../../../../../src/gcc-8.1.0/libgcc/config/i386/cygwin.S:126 126 ../../../../../src/gcc-8.1.0/libgcc/config/i386/cygwin.S: No such file or directory. 参考&#xff1a;不同平台下对…

代码随想录训练营Day 46|力扣完全背包、518. 零钱兑换 II、377. 组合总和 Ⅳ

1.完全背包 视频讲解&#xff1a;带你学透完全背包问题&#xff01; 和 01背包有什么差别&#xff1f;遍历顺序上有什么讲究&#xff1f;_哔哩哔哩_bilibili https://programmercarl.com/%E8%83%8C%E5%8C%85%E9%97%AE%E9%A2%98%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80%E5%AE%8C%E…

as keyof GlobalStore

解释 as keyof GlobalStore 在 TypeScript 中&#xff0c;as keyof GlobalStore 是一种类型断言语法。它告诉 TypeScript&#xff0c;返回的值是一个特定类型的值&#xff0c;这里是 GlobalStore 类型的键。这在编译时有助于确保类型安全。 关键点&#xff1a; 类型断言&…

【三国战纪game】

编写一个完整的《三国战纪》游戏是一个庞大的项目&#xff0c;需要一个专业的游戏开发团队和大量的时间。但是&#xff0c;我可以给你一个简化的概念&#xff0c;帮助你开始这个过程。 游戏概念&#xff1a;三国战纪 《三国战纪》是一个以中国三国时期为背景的策略游戏&#…

构建智慧银行保险系统的先进技术架构

随着科技的不断发展&#xff0c;智慧银行保险系统正日益受到关注。在这个数字化时代&#xff0c;构建一个先进的技术架构对于智慧银行保险系统至关重要。本文将探讨如何构建智慧银行保险系统的先进技术架构&#xff0c;以提升服务效率、降低风险并满足客户需求。 ### 1. 智慧银…

qwen-moe

一、定义 qwen-moe 代码讲解&#xff0c; 代码qwen-moe与Mixtral-moe 一样&#xff0c; 专家模块qwen-moe 开源教程Mixture of Experts (MoE) 模型在Transformer结构中如何实现&#xff0c;Gate的实现一般采用什么函数&#xff1f; Sparse MoE的优势有哪些&#xff1f;MoE是如…

C++学习 const 上

&#x1f308; C Primer 的学习笔记 前言 这篇blog 主要是想具体讲讲新学到的const 当然不止是const 而是基于这个const引申出来的指针和引用。还是需要捋一捋的&#xff0c;这还是有点困难的。 我会把每一节的重点都摘出来&#xff0c;放在前面~ 1️⃣首先讲讲const 2️⃣…

Linux C/C++动态库制作

概念&#xff1a;程序在编译时不会把库文件的二进制代码链接到目标程序中&#xff0c;而是在运行时候才被载入。 如果多个进程中用到了同一动态库中的函数或类&#xff0c;那么在内存中只有一份&#xff0c;避免了空间浪费问题。 特点&#xff1a; 程序运行在运行的过程中&…

统计信号处理基础 习题解答10-6

题目 在例10.1中&#xff0c;把数据模型修正为&#xff1a; 其中是WGN&#xff0c;如果&#xff0c;那么方差&#xff0c;如果&#xff0c;那么方差。求PDF 。把它与经典情况PDF 进行比较&#xff0c;在经典的情况下A是确定性的&#xff0c;是WGN&#xff0c;它的方差为&#…

5.算法讲解之-二分查找(简单易懂)

1.简介 1.二分查找的思路简单易懂&#xff0c;较难的是如何处理查找过程中的边界条件&#xff0c;当较长时间没写二分查找的时候就容易忘记如何处理边界条件。 2.只有多写代码&#xff0c;多做笔记就不易忘记边界条件 2.算法思路 正常查找都是从头到尾查找一个数字是否在数组中…

使用pycharm+opencv进行视频抽帧(可以用来扩充数据集)+ labelimg的使用(数据标准)

一.视频抽帧 1.新创建一个空Pycharm项目文件&#xff0c;命名为streach zhen 注&#xff1a;然后要做一个前期工作 创建opencv环境 &#xff08;1&#xff09;我们在这个pycharm项目的终端里面输入下面的命令&#xff1a; pip install opencv-python --user -i https://pypi.t…

SettingWithCopyWarning: A value is trying to be set on a copy of a slice fro

SettingWithCopyWarning: A value is trying to be set on a copy of a slice fro 错误代码&#xff1a; while i < len(data_csv_data):if data_csv_data[flowmember][i] j:data_csv_data[label][i] data_csv_label[label][j-1]data_csv_data[classes][i]data_csv_label[…

[数据集][目标检测]猕猴桃检测数据集VOC+YOLO格式1838张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;1838 标注数量(xml文件个数)&#xff1a;1838 标注数量(txt文件个数)&#xff1a;1838 标注…