OpenCV官方教程中文版 —— 分水岭算法图像分割

news2024/11/19 15:15:41

OpenCV官方教程中文版 —— 分水岭算法图像分割

  • 前言
  • 一、原理
  • 二、示例
  • 三、完整代码

前言

本节我们将要学习

使用分水岭算法基于掩模的图像分割

函数:cv2.watershed()

一、原理

任何一副灰度图像都可以被看成拓扑平面,灰度值高的区域可以被看成是山峰,灰度值低的区域可以被看成是山谷。我们向每一个山谷中灌不同颜色的水。随着水的位的升高,不同山谷的水就会相遇汇合,为了防止不同山谷的水汇合,我们需要在水汇合的地方构建起堤坝。不停的灌水,不停的构建堤坝知道所有的山峰都被水淹没。我们构建好的堤坝就是对图像的分割。这就是分水
岭算法的背后哲理。

但是这种方法通常都会得到过度分割的结果,这是由噪声或者图像中其他不规律的因素造成的。为了减少这种影响,OpenCV 采用了基于掩模的分水岭算法,在这种算法中我们要设置那些山谷点会汇合,那些不会。这是一种交互式的图像分割。我们要做的就是给我们已知的对象打上不同的标签。如果某个区域肯定是前景或对象,就使用某个颜色(或灰度值)标签标记它。如果某个
区域肯定不是对象而是背景就使用另外一个颜色标签标记。而剩下的不能确定是前景还是背景的区域就用 0 标记。这就是我们的标签。然后实施分水岭算法。每一次灌水,我们的标签就会被更新,当两个不同颜色的标签相遇时就构建堤坝,直到将所有山峰淹没,最后我们得到的边界对象(堤坝)的值为 -1。

二、示例

下面的例子中我们将就和距离变换和分水岭算法对紧挨在一起的对象进行分割。如下图所示,这些硬币紧挨在一起。就算你使用阈值操作,它们任然是紧挨着的。
在这里插入图片描述

# -*- coding: utf-8 -*-
import cv2
from matplotlib import pyplot as plt
img = cv2.imread('water_coins.png')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
print("阈值为:", ret)
plt.figure()
plt.subplot(121), plt.imshow(gray, cmap='gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(thresh, cmap='gray')
plt.title('Binary Image'), plt.xticks([]), plt.yticks([])
plt.show()

在这里插入图片描述

现在我们要去除图像中的所有的白噪声。这就需要使用形态学中的开运算。为了去除对象上小的空洞我们需要使用形态学闭运算。所以我们现在知道靠近对象中心的区域肯定是前景,而远离对象中心的区域肯定是背景。而不能确定的区域就是硬币之间的边界。

所以我们要提取肯定是硬币的区域。腐蚀操作可以去除边缘像素。剩下就可以肯定是硬币了。当硬币之间没有接触时,这种操作是有效的。但是由于硬币之间是相互接触的,我们就有了另外一个更好的选择:距离变换再加上合适的阈值。接下来我们要找到肯定不是硬币的区域。这是就需要进行膨胀操作了。膨胀可以将对象的边界延伸到背景中去。这样由于边界区域被去处理,我们就可以知道那些区域肯定是前景,那些肯定是背景。如下图所示。
在这里插入图片描述剩下的区域就是我们不知道该如何区分的了。这就是分水岭算法要做的。这些区域通常是前景与背景的交界处(或者两个前景的交界)。我们称之为边界。从肯定是不是背景的区域中减去肯定是前景的区域就得到了边界区域。

如结果所示,在阈值化之后的图像中,我们得到了肯定是硬币的区域,而且硬币之间也被分割开了。(有些情况下你可能只需要对前景进行分割,而不需要将紧挨在一起的对象分开,此时就没有必要使用距离变换了,腐蚀就足够了。当然腐蚀也可以用来提取肯定是前景的区域。)

现在知道了那些是背景那些是硬币了。那我们就可以创建标签(一个与原图像大小相同,数据类型为 int32 的数组),并标记其中的区域了。对我们已经确定分类的区域(无论是前景还是背景)使用不同的正整数标记,对我们不确定的区域使用 0 标记。我们可以使用函数 cv2.connectedComponents()
来做这件事。它会把将背景标记为 0,其他的对象使用从 1 开始的正整数标记。

但是,我们知道如果背景标记为 0,那分水岭算法就会把它当成未知区域了。所以我们想使用不同的整数标记它们。而对不确定的区域(函数cv2.connectedComponents 输出的结果中使用 unknown 定义未知区域)标记为 0。

# Marker labelling
ret, markers1 = cv2.connectedComponents(sure_fg)
# Add one to all labels so that sure background is not 0, but 1
markers = markers1+1
# Now, mark the region of unknown with zero
markers[unknown==255] = 0

结果使用 JET 颜色地图表示。深蓝色区域为未知区域。肯定是硬币的区域使用不同的颜色标记。其余区域就是用浅蓝色标记的背景了。

现在标签准备好了。到最后一步:实施分水岭算法了。标签图像将会被修改,边界区域的标记将变为 -1.

markers3 = cv2.watershed(img,markers)
img[markers3 == -1] = [255,0,0]

结果如下。有些硬币的边界被分割的很好,也有一些硬币之间的边界分割的不好。
在这里插入图片描述

三、完整代码

# -*- coding: utf-8 -*-
import cv2
from matplotlib import pyplot as plt
import numpy as np
img = cv2.imread('water_coins.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(
    gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)

plt.figure()
plt.subplot(121), plt.imshow(gray, cmap='gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(thresh, cmap='gray')
plt.title('Binary Image'), plt.xticks([]), plt.yticks([])

# noise removal
kernel = np.ones((3, 3), np.uint8)
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=3)
# sure background area
sure_bg = cv2.dilate(opening, kernel, iterations=5)
plt.figure()
plt.subplot(121), plt.imshow(opening, cmap='gray')
plt.title('opening Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(sure_bg, cmap='gray')
plt.title('sure_bg Image'), plt.xticks([]), plt.yticks([])
# Finding sure foreground area
# 距离变换的基本含义是计算一个图像中非零像素点到最近的零像素点的距离,也就是到零像素点的最短距离
# 个最常见的距离变换算法就是通过连续的腐蚀操作来实现,腐蚀操作的停止条件是所有前景像素都被完全
# 腐蚀。这样根据腐蚀的先后顺序,我们就得到各个前景像素点到前景中心呗Ⅵ像素点的
# 距离。根据各个像素点的距离值,设置为不同的灰度值。这样就完成了二值图像的距离变换
# cv2.distanceTransform(src, distanceType, maskSize)
# 第二个参数 0,1,2 分别表示 CV_DIST_L1, CV_DIST_L2 , CV_DIST_C
dist_transform = cv2.distanceTransform(opening, 1, 5)
ret, sure_fg = cv2.threshold(dist_transform, 0.6 * dist_transform.max(), 255, 0)
# Finding unknown region
sure_fg = np.uint8(sure_fg)
unknown = cv2.subtract(sure_bg, sure_fg)

plt.figure()
plt.subplot(121), plt.imshow(dist_transform, cmap='gray')
plt.title('dist_transform Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(sure_fg, cmap='gray')
plt.title('threshold Image'), plt.xticks([]), plt.yticks([])

# Marker labelling
ret, markers1 = cv2.connectedComponents(sure_fg)
# Add one to all labels so that sure background is not 0, but 1
markers = markers1+1
# Now, mark the region of unknown with zero
markers[unknown==255] = 0
markers3 = cv2.watershed(img,markers)
img[markers3 == -1] = [0,0,255]

b,g,r = cv2.split(img)
img2 = cv2.merge([r,g,b])

plt.figure()
plt.subplot(121), plt.imshow(markers3)
plt.title('marker Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(img2)
plt.title('result'), plt.xticks([]), plt.yticks([])
plt.show()

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

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

相关文章

企业知识库知识分类太有必要了,是省时省力的关键!

企业知识库是存储、组织和共享企业内部知识的重要工具。在现代企业中,知识是一项宝贵的资产,对于提高企业的竞争力和创新能力至关重要。而通过企业知识库进行知识分类,可以将海量信息有序划分和组织,让企业员工能够快速定位、理解…

贪心算法学习------优势洗牌

目录 一,题目 二,题目接口 三,解题思路和代码 全部代码: 一,题目 给定两个数组nums1和nums2,nums1相对于nums2的优势可以用满足nums1[i]>nums2[i]的索引i的数目来描述。 返回nums1的任意排序,使其优…

标签推荐Top-N列表优化算法_朱小兵

2算法模型 2.1 Top-N推荐列表重排序算法

AIGC - Qwen大模型:Qwen-7B模型推理部署

硬件环境 作为AIGC方面的小白来说,我抱着非常天真的想法,想让它在我的工作笔记本上用i5的CPU去跑,至于为什么这么想,当然是因为我没有GPU,身边也没有其他的带显卡电脑 恰好,在腾讯云看到了GN7的显示优惠活…

Android裁剪图片之后无法加载的问题

适配Android11之后更改了图片保存目录,导致裁剪之后图片一直无法加载(fileNotfound) 最主要的问题在于保存裁剪文件的目录不能为私有目录,因为裁剪工具是系统工具,无法直接访问项目本身的私有目录。 解决办法&#x…

算法---缺失的第一个正数

题目 给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。 请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。示例 1:输入:nums [1,2,0] 输出:3 示例 2:输入:nums …

一文6个概念带你成功入门自动化测试

自动化测试有以下几个概念: 单元测试集成测试E2E 测试快照测试测试覆盖率TDD 以及 BDD 等 简述 项目开发过程中会有几个经历。 版本发布上线之前,会有好几个小时甚至是更长时间对应用进行测试,这个过程非常枯燥而痛苦代码的复杂度达到了一…

Linux 环境变量 二

目录 获取环境变量的后两种方法 环境变量具有全局属性 内建命令 和环境变量相关的命令 c语言访问地址 重新理解地址 地址空间 获取环境变量的后两种方法 main函数的第三个参数 :char* env[ ] 也是一个指针数组,我们可以把它的内容打印出来看看。 …

线上机器cpu负载200%告警排查

CPU告警排查思路,正常情况就是如下两种情况 执行任务的java线程本身存在bug,死循环或者操作本身耗cpu,导致cpu占用过高jvm发生频繁gc,导致cpu过高 查看耗时较高的进程 top命令,并按大写P以cpu利用率排序,确…

学习笔记3——JVM基础知识

学习笔记系列开头惯例发布一些寻亲消息 链接:https://baobeihuijia.com/bbhj/contents/3/196593.html JVM(Write Once,Run Anywhere) 以下是一些学习时有用到的资料,只学习了JVM的基础知识,对JVM整体进…

Nginx 部署多个安全域名,多个服务【工作记录】

以下是本人通过Docker 部署的Nginx挂载出来的文件目录 先看下 nginx.conf 配置文件内容:如下 ps:当前文件就是安装后的初始内容,无修改。主要关注最后一行 include /etc/nginx/conf.d/*.conf;表示引入其他目录下的.conf配置文件;…

构建 hive 时间维表

众所周知 hive 的时间处理异常繁琐且在一些涉及日期的统计场景中会写较长的 sql,例如:周累计、周环比等;本文将使用维表的形式降低时间处理的复杂度,提前计算好标准时间字符串未来可能需要转换的形式。 一、表设计 结合业务场景常…

MySQL(5):排序与分页

排序数据 排序规则 使用 ORDER BY 子句排序 ASC(ascend):升序 DESC(descend):降序 ORDER BY 子句在SELECT语句的结尾 单列排序 SELECT last_name,job_id,department_id,hire_date FROM employees ORDER…

设计师看后惊叹:当泥色走进室内空间的时候竟有此番效果

在美国的洛杉矶,一座引人注目的度假别墅彰显了自己的独特魅力。建筑师 Ron Radziner 说:“这是一座现代住宅,采用风化木、深色灰泥和绿色屋顶等与峡谷环境相关的材料,设计得通透、开放。” Zwickl 解释说:"他们想…

CCF CSP认证 历年真题自练Day42

题目 试题编号: 201512-3 试题名称: 画图 时间限制: 1.0s 内存限制: 256.0MB 问题描述: 问题描述   用 ASCII 字符来画图是一件有趣的事情,并形成了一门被称为 ASCII Art 的艺术。例如,下图…

正点原子嵌入式linux驱动开发——Linux CAN驱动

CAN是目前应用非常广泛的现场总线之一,主要应用于汽车电子和工业领域,尤其是汽车领域,汽车上大量的传感器与模块都是通过CAN总线连接起来的。CAN总线目前是自动化领域发展的热点技术之一,由于其高可靠性,CAN总线目前广…

axios中get/post请求方式

1. 前言 最近突然发现post请求可以使用params方式传值,然后想总结一下其中的用法。 2.1 分类 get请求中没有data传值方式 经过查阅资料,get请求是可以通过body传输数据的,但是许多工具类并不支持此功能。 在postman中,选择get请求…

WebService接口方式和Restful接口这两者有什么区别和相同点

WebService和RESTful接口都是用于在网络上进行通信和数据交换的技术,但它们在设计和使用上有一些重要的区别和相似之处。 相同点: 基于HTTP协议:无论是WebService还是RESTful接口,它们都是通过HTTP协议进行通信的。 支持多种数据…

高防服务器与CDN防御怎么区分?

​  高防服务器和CDN防御是两种不同的网络安全防护方式。 定义上:高防服务器是指在传统服务器的基础上,增加了一系列的防御措施,如DDoS防护、WAF防护、IP黑白名单等,以提高服务器的抗攻击能力。高防服务器通常具有较强的计算和存…

知识图谱实战应用30-知识图谱在反欺诈情报分析项目中的应用实践

大家好,我是微学AI,今天给大家介绍一下知识图谱实战应用30-知识图谱在反欺诈情报分析项目中的应用实践,现代商业环境中,各类欺诈行为日益猖獗,严重影响企业的运营和社会秩序。传统的欺诈检测方法难以满足实时性和有效性方面的要求。本文介绍了采用知识图谱技术构建反欺诈情报…