用OpenCV进行传统图像分割

news2024/11/15 17:16:42

1. 引言

欢迎回来,我的图像处理爱好者们!本文我们将直接进入传统图像分析的新领域——图像分割,这是指将图像分成若干具有相似性质的区域的过程,从数学角度来看,图像分割是将图像划分成互不相交的区域的过程。
闲话少说,我们直接开始吧!

2. 基于阈值的分割

首先介绍的是基于阈值和基于Otsu的分割方法,这是我们进行分割实验的第一步,我们将了解像这样简单而有效的方法是如何根据图像像素的强度值将图像划分为前景和背景两部分的。

但是我们如何科学地决定分割的阈值呢?这就是Otsu方法派上用场的地方。简单地说,这种巧妙的方法通过计算出最大化类间方差的最佳阈值,使其成为自适应阈值选择的优秀工具。

首先从我们的准备工作开始, 导入我们需要的图像处理库:

# Import libraries
from skimage.io import imread, imshow
import matplotlib.pyplot as plt
import numpy as np
from skimage.color import rgb2gray, rgb2hsv
from skimage.filters import threshold_otsu

加载本节测试图像样例,如下:

# Display the image that we will be playing with
original_image = imread('plants.jpg')
plt.figure(figsize=(20,20))
plt.imshow(original_image)
plt.title('Original Image', fontsize=20, weight='bold')
plt.axis('off')
plt.show()

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

3. 固定阈值分割

当我们想要对图像进行二值化时,我们执行两步过程,将图像转换为灰度图,然后设置我们想要的任何阈值(通常为0.50),进而将其转换为二值图像:

# Convert the image to grayscale
gray_image = rgb2gray(original_image)
plt.figure(figsize=(20,20))
plt.imshow(gray_image, cmap='gray')
plt.title('Grayscale Image', fontsize=20, weight='bold')
plt.axis('off')
plt.show()

得到灰度图,结果如下:在这里插入图片描述

选择固定阈值,进行二值化,代码如下:

# Convert the grayscale image to binary image using threshold=0.50
threshold = 0.5
binary_image = gray_image<threshold
plt.figure(figsize=(20,20))
plt.imshow(binary_image, cmap='gray')
plt.title('Binary Image', fontsize=20, weight='bold')
plt.axis('off')
plt.show()

得到二值图效果如下:
在这里插入图片描述

观察上图,可以发现对于前景背景区分度比较明显的图像,通过设置固定的阈值也可以方便的得到我们需要的前景物体。

4. 自适应阈值分割

请注意分析上述图像中原本较亮的部分是如何变黑的,这是因为我们使用了0.50作为阈值。但在Otsu的方法中,我们不必手动设置阈值,我们可以通过调用函数threshold_otsu让Otsu为我们处理这项工作,如下所示:

# Use threshold_otsu to automatically calculate the optimal threshold
threshold = threshold_otsu(gray_image)
print(f"Otsu's Threshold: {threshold:.2f}")
binary_image_otsu  = gray_image < threshold
plt.figure(figsize=(20,20))
plt.imshow(binary_image_otsu, cmap='gray')
plt.title("Binary Image using Otsu's Method", fontsize=20, weight='bold')
plt.axis('off')
plt.show()

结果如下:

Output:
Otsu's Threshold: 0.67

得到二值图的效果如下:
在这里插入图片描述
看到区别了吗?我们不必为了获得更好的二值化图像而手动地对阈值进行试错。多亏了otsu的方法,为我们节省了大量试错时间!

5. 彩色图像分割

假设我们只想在图像中隔离青色的植物,那么是时候采用另一种令人兴奋的技术了——彩色图像分割!这种方法是二值化的扩展,但这里有一个注意项:我们使用颜色信息来分离对象,而不是像素值强度。当图像中感兴趣的对象拥有独特不同的颜色时,该方法特别有用。

按照惯例,我们还是首先导入我们的样例图像,代码如下:

# Display original image
original_image = imread('plants.jpg')
plt.figure(figsize=(20,20))
imshow(original_image)
plt.title('Original Image', fontsize=20, weight='bold')
plt.axis('off')
plt.show()

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

6. 基于RGB颜色空间的图像分割

观察上图,为了只隔离茂盛的绿色植物,我们可以执行简单的分割操作。如果绿色通道值大于红色和蓝色通道值,我们可以使用下面的比较运算符来表示像素为绿色,代码如下:

# Read image using skimage
original_image = imread('plants.jpg')

# Create subplot of 1x2
fig, ax = plt.subplots(1, 2, figsize=(20, 20))

# Plot original image
ax[0].imshow(original_image)
ax[0].set_title('Original Image', fontsize=20, weight='bold')
ax[0].axis('off')

# Get red, green, and blue channels
r = original_image[:, :, 0]
g = original_image[:, :, 1]
b = original_image[:, :, 2]

# Create a mask for green color
mask = (g > r) & (g > b) # adjust these values depending on what you consider to be 'green'

# Create a new image
new_img = original_image.copy()

# Apply mask to all channels
new_img[:, :, 0] = new_img[:, :, 0] * mask
new_img[:, :, 1] = new_img[:, :, 1] * mask
new_img[:, :, 2] = new_img[:, :, 2] * mask

# Plot the green segmented image
ax[1].imshow(new_img)
ax[1].set_title('Green Segmented Image', fontsize=20, weight='bold')
ax[1].axis('off')

# Display the subplot
plt.tight_layout()
plt.show()

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

7. 美化效果

观察上述右图,虽然我们可以大致得到我们想要的分割结果,但是仔细观察上图中留下了很多无用的白色标记。实质上白色是红色、绿色和蓝色都达到顶峰的颜色。因此,为了阻止这些不想要的因素,我们可以在代码中添加一个白色掩膜,代码如下:

# Create a mask for white color
white_threshold = 180  # adjust this depending on what you consider to be 'white'
white_mask = (r > white_threshold) & (g > white_threshold) & (b > white_threshold)
# Combine the green and white masks
mask = green_mask & ~white_mask  # ~ is the NOT operator
# Create a new image and apply mask
new_img = original_image.copy()
# Apply mask to all channels
new_img[:,:,0] = new_img[:,:,0] * mask
new_img[:,:,1] = new_img[:,:,1] * mask
new_img[:,:,2] = new_img[:,:,2] * mask

# Plot the green segmented image
ax[1].imshow(new_img)
ax[1].set_title('Green Segmented Image', fontsize=20, weight='bold')
ax[1].axis('off');

得到结果如下:
在这里插入图片描述

8. 基于HSV颜色空间的图像分割

如上所述,有时在其他颜色空间(如HSV(色调、饱和度、值))中分割颜色更容易。在HSV颜色空间中,不同的颜色排列在一个圆上(色调),因此挑选特定的颜色可能更容易。

让我们显示原始图像的色调、饱和度和值:

# Read image using skimage
original_image = imread('plants.jpg')

# Convert the image to HSV color space
hsv_img = rgb2hsv(original_image)

fig, ax = plt.subplots(1, 3, figsize=(20,20))
ax[0].imshow(hsv_img[:,:,0], cmap='hsv')
ax[0].set_title('Hue', fontsize=20)
ax[1].imshow(hsv_img[:,:,1], cmap='hsv')
ax[1].set_title('Saturation', fontsize=20)
ax[2].imshow(hsv_img[:,:,2], cmap='hsv')
ax[2].set_title('Value', fontsize=20);

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

9. 基于H色调的图像分割

由于使用上面的图很难看到强度值的差异,所以让我们使用colorbar函数来分析,代码如下:

plt.imshow(hsv_img[:,:,0], cmap='hsv')
plt.colorbar()

结果如下:
在这里插入图片描述
正如你所看到的,橙色介于0到0.05之间,所以让我们这些值作为阈值:

# Read image using skimage
original_image = imread('plants.jpg')

# Convert the image to HSV color space
hsv_img = rgb2hsv(original_image)

# Create a mask for orange color
# Hue for orange is roughly in the range of 0 - 0.05
# We can play around these values to adapt to our specific color requirement
mask = (hsv_img[:,:,0] > 0) & (hsv_img[:,:,0] < 0.05)

# create a new image and apply mask
new_img = original_image.copy()

new_img[:,:,0] = new_img[:,:,0] * mask
new_img[:,:,1] = new_img[:,:,1] * mask
new_img[:,:,2] = new_img[:,:,2] * mask

# plot the original and segmented images side by side
fig, ax = plt.subplots(1, 2, figsize=(20,10))

ax[0].imshow(original_image)
ax[0].set_title('Original Image', fontsize=20, weight='bold')
ax[0].axis('off')

ax[1].imshow(new_img)
ax[1].set_title('Orange Segmented Image', fontsize=20, weight='bold')
ax[1].axis('off')

plt.show()

结果如下:
在这里插入图片描述
观察上图,我们已经成功地在植物图像中分割出了具有鲜艳色调的橙色植物。

10.总结

哇,我们今天的旅程简直太棒了!我们对图像分割的深入研究无疑为我们如何从图像中提取有意义的信息提供了有效的方法。 希望大家可以通过上述案例,进入到图像处理的神秘领域,感叹相关算法的魅力。

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

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

相关文章

上海亚商投顾:沪指高开高走涨1.31% 汽车整车板块领涨

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 市场情绪 三大指数今日集体反弹&#xff0c;沪指全天高开高走&#xff0c;深成指、创业板指午后有所回落。中字头及以保险为…

3.FreeRTOS系统源码移植

目录 一、获取FreeRTOS源代码 二、FreeRTOS系统源码内容 三、FreeRTOS系统源码移植 一、获取FreeRTOS源代码 来FreeRTOS官方网站:https://www.freertos.org/ 我这里主要提供的是例程为FreeRTOS的V10.4.6版本 1、进入官网&#xff0c;点击Download FreeRTOS 2、点击Downl…

数分面试题-SQL高频考点

目录标题 1、SQL语言分类2、join连接3、列转换3.1 列转行3.2 行转列 4、分页查询5、字符串处理函数5.1 字符函数5.2 数学函数5.3 日期函数 6、索引6.1 什么是索引6.2 建立索引的优缺点6.3 索引有哪些6.4 索引为什么快6.5 什么情况下加索引6.6 怎么知道索引用没用上6.7 用过组合…

Axure教程—中继器删除与批量删除

本文介绍的是用Axure中的中继器实现删除与批量删除效果 效果 功能 1、选中某项数据删除&#xff0c;删除后提示“删除成功” 2、选择多项数据删除&#xff0c;删除后提示“删除成功”&#xff0c;如果不选取数据&#xff0c;点击”批量删除“按钮&#xff0c;提示”请至少选择…

SNMP 计算机网络管理 一文理清-管理信息库,OID,MIB结构树,SNMP协议体系结构

⬜⬜⬜ &#x1f430;&#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea;(*^▽^*)欢迎光临 &#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea;&#x1f430;⬜⬜⬜ ✏️write in front✏️ &#x1f4dd;个人主页&#xff1a;陈丹宇jmu &am…

Java接口详解

目录 接口方法 接口的属性 接口方法 在Java设计的时候, 我们所说的接口,不同于类,我们尝尝希望一个类能满足某个特定的功能,或者需求. 我们在使用Arrays类中的sort方法对对象数组进行排序,但是对象所属的类必须实现Comparable接口: 可以看到里面只有一个方法: public inter…

【动态规划算法】第二题:⾯试题08.01.三步问题

&#x1f496;作者&#xff1a;小树苗渴望变成参天大树 &#x1f389;作者宣言&#xff1a;认真写好每一篇博客 &#x1f38a;作者gitee:gitee 如 果 你 喜 欢 作 者 的 文 章 &#xff0c;就 给 作 者 点 点 关 注 吧&#xff01; 文章目录 前言 前言 今天我们开始讲解动态规…

MySQL数据库总结 之 约束(restraint) 外键约束

前三篇关于MySQL的博客&#xff0c;地址如下&#xff1a; 1. MySQL数据库 && SQL语言命令总结 && 数据类型、运算符和聚合函数汇总_Flying Bulldog的博客-CSDN博客 2. 从0到1 && 关于MySQL的数据库和表_Flying Bulldog的博客-CSDN博客 3. MySQL数据…

Protobuf实现序列化和反序列化详细步骤

步骤1&#xff1a;添加对应的依赖 <dependency><groupId>com.google.protobuf</groupId><artifactId>protobuf-java</artifactId><version>3.7.1</version> </dependency>步骤2&#xff1a;编写bulid.bat文件执行&#xff0c…

react组件--npm发包总过程(超全教程!建议收藏!)

npm发包总过程 npm账号登录注册&#xff08;已有账号可跳过&#xff09;登录验证是否登录成功创建组件项目目录图新建文件夹&#xff0c;并初始化安装依赖/src/index.js --打包入口文件src/components/button/index.js --组件逻辑代码src/components/button/index.css --组件逻…

10 次面试 9 次被刷?吃透这 500 道大厂 Java 高频面试题后,怒斩 offer

很多 Java 工程师的技术不错&#xff0c;但是一面试就头疼&#xff0c;10 次面试 9 次都是被刷&#xff0c;过的那次还是去了家不知名的小公司。 问题就在于&#xff1a;面试有技巧&#xff0c;而你不会把自己的能力表达给面试官。 应届生&#xff1a;你该如何准备简历&#x…

verilog HDL -生成块 - generate——endgenerate

参考&#xff1a;vrilog数字系统设计 夏宇闻 【第3版】 5.7 生成块理解 生成语句可以动态的生成verilog代码&#xff0c;方便参数化模块的生成&#xff0c;大大的简化程序的编写过程,常用于以下情况&#xff1a; 对矢量中的多个位进行重复操作进行多个模块的实例引用的重复操…

Anacoda3中成功配置Tesseract-OCR超详细教程!!(win7、win10)

Anacoda3中成功配置Tesseract-OCR Tesseract-OCR简介安装及配置环境步骤1、在Anaconda虚拟环境中安装pytesseract第三方库2、下载tesseract-ocr安装包并安装3、环境配置检验是否安装成功修改pytesseract.py文件&#xff08;很重要&#xff01;&#xff09; Tesseract-OCR简介 …

C语言常用的数据转换函数

编程时&#xff0c;经常用到进制转换、字符转换。现整理记录如下&#xff1a; 一、字符串转十六进制 void StrToHex(char *pbDest, char *pbSrc, int nLen) {char h1,h2;char s1,s2;int i;for (i0; i<nLen/2; i){h1 pbSrc[2*i];h2 pbSrc[2*i1];s1 toupper(h1) - 0x30; …

ASP.NET Core MVC -- 控制器

添加控制器 访问测试 默认控制器访问index public string Index() {return "This is my default action..."; }特定访问路径 public string Welcome() {return "This is the Welcome action method..."; } 特定路径访问&#xff0c;带参数 public str…

查询Oracle当前用户下,所有数据表的总条数

需求&#xff1a;查询Oracle当前用户下&#xff0c;所有数据表的总条数 方法&#xff1a;存储过程 右键点击Procedures&#xff0c;点击New 点击OK 把存储过程写进去&#xff0c;然后点击编译运行&#xff1a; create or replace procedure tables_count ist_count numbe…

dolphinscheduler的switch组件

目录 一、背景 switch组件官方文档 Switch简介 创建任务 任务参数 二、实操DEMO SQL任务switch判断DEMO 第一步&#xff1a;新建SQL任务&#xff0c;配置好参数 第二步&#xff1a; 定义SWITCH节点 三、参考资料 默认任务参数 一、背景 Apache DolphinScheduler 是…

B站广告投放,B站有哪些投放广告的方式比较靠谱?

随着各视频平台发展&#xff0c;B站也逐渐壮大&#xff0c;成为当下最受年轻人喜爱的社交媒体平台之一&#xff0c;B站根据年轻人的喜好提供了多样化的产品和服务&#xff0c;如视频、直播、游戏、漫画、影业、演出活动、专栏等。形成了自己独特的up主文化。B站拥有着高黏性和高…

如何保证消息的可靠性传输?或者说,如何处理消息丢失的问题?

这个是肯定的&#xff0c;用 MQ 有个基本原则&#xff0c;就是数据不能多一条&#xff0c;也不能少一条&#xff0c;不能多&#xff0c;就是 前面说的重复消费和幂等性问题。不能少&#xff0c;就是说这数据别搞丢了。那这个问题你必须得考 虑一下。 如果说你这个是用 MQ 来传…

基于单片机心率脉搏心率血压体温血氧检测系统的设计与实现

功能介绍 本次设计通过32系列单片机STM32进行数据处理&#xff0c;配置引脚和JFC103传感器以及温度传感器进行数据通信。采用防水DS18B20进行腋下温度采集&#xff0c;通过单总线方式进行数据传输。心率血氧血压模块通过串口通信方式把采集到的数据发送给单片机&#xff0c;所有…