【OpenCV学习笔记16】- 图像阈值

这是对于 OpenCV 官方文档中 图像处理 的学习笔记。学习笔记中会记录官方给出的例子,也会给出自己根据官方的例子完成的更改代码,同样彩蛋的实现也会结合多个知识点一起实现一些小功能,来帮助我们对学会的知识点进行结合应用。
如果有喜欢我笔记的请麻烦帮我关注、点赞、评论。谢谢诸位。

学习笔记:
学习笔记目录里面会收录我关于OpenCV系列学习笔记博文,大家如果有什么不懂的可以通过阅读我的学习笔记进行学习。
【OpenCV学习笔记】- 学习笔记目录

内容

  • 你会学到简单阈值法,自适应阈值法,以及 Otsu 阈值法(俗称大津法)等。
  • 你会学到如下函数:cv.threshold,cv.adaptiveThreshold 等。

简单阈值法

此方法是直截了当的。如果像素值大于阈值,则会被赋为一个值(可能为白色),否则会赋为另一个值(可能为黑色)。
使用的函数是 cv2.threshold。

  • 第一个参数是源图像,它应该是灰度图像。
  • 第二个参数是阈值,用于对像素值进行分类。
  • 第三个参数是 maxval,它表示像素值大于(有时小于)阈值时要给定的值。

opencv 提供了不同类型的阈值,由函数的第四个参数决定。不同的类型有:

  • cv2.THRESH_BINARY
  • cv2.THRESH_BINARY_INV
  • cv2.THRESH_TRUNC
  • cv2.THRESH_TOZERO
  • cv2.THRESH_TOZERO_INV

文档清楚地解释了每种类型的含义。请查看文档。

获得两个输出。第一个是 retval,稍后将解释。第二个输出是我们的阈值图像。
示例代码:

# 图像阈值
# 简单阈值法
import cv2
from matplotlib import pyplot as plt

img = cv2.imread('../image/3.1-test.jpg', 0)
ret, thresh1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
ret, thresh2 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV)
ret, thresh3 = cv2.threshold(img, 127, 255, cv2.THRESH_TRUNC)
ret, thresh4 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO)
ret, thresh5 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO_INV)
titles = ['Original Image', 'BINARY', 'BINARY_INV', 'TRUNC', 'TOZERO', 'TOZERO_INV']
images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]
for i in range(6):
    plt.subplot(2, 3, i + 1), plt.imshow(images[i], 'gray')
    plt.title(titles[i])
    plt.xticks([]), plt.yticks([])
plt.show()

效果图:
在这里插入图片描述

注意: 绘制多个图像,我们使用 plt.subplot()函数。有关详细信息,请查看 Matplotlib 文档。

自适应阈值

在前一节中,我们使用一个全局变量作为阈值。但在图像在不同区域具有不同照明条件的条件下,这可能不是很好。在这种情况下,我们采用自适应阈值。在此,算法计算图像的一个小区域的阈值。因此,我们得到了同一图像不同区域的不同阈值,对于不同光照下的图像,得到了更好的结果。
cv2.adaptiveThreshold 它有三个“特殊”输入参数,只有一个输出参数。

  • Adaptive Method : 它决定如何计算阈值。
    • cv2.ADAPTIVE_THRESH_MEAN_C 阈值是指邻近地区的平均值。
    • cv2.ADAPTIVE_THRESH_GAUSSIAN_C 阈值是权重为高斯窗的邻域值的加权和。
  • Block Size : 它决定了计算阈值的窗口区域的大小。
  • C : 它只是一个常数,会从平均值或加权平均值中减去该值。

下面的代码比较了具有不同照明的图像的全局阈值和自适应阈值:

示例代码:

# 图像阈值
# 自适应阈值
import cv2
from matplotlib import pyplot as plt

img = cv2.imread('../image/3.1-test.jpg', 0)
# 降噪
img = cv2.medianBlur(img, 5)
ret, th1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
th2 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 2)
th3 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2)
titles = ['Original Image', 'Global Thresholding (v = 127)',
          'Adaptive Mean Thresholding', 'Adaptive Gaussian Thresholding']
images = [img, th1, th2, th3]
for i in range(4):
    plt.subplot(2, 2, i + 1), plt.imshow(images[i], 'gray')
    plt.title(titles[i])
    plt.xticks([]), plt.yticks([])
plt.show()

效果图:
在这里插入图片描述

Otsu 二值化

在第一部分中,我告诉过您有一个参数 retval。当我们进行 Otsu 二值化时,它的用途就来了。那是什么?

在全局阈值化中,我们使用一个任意的阈值,对吗?那么,我们如何知道我们选择的值是好的还是不好的呢?答案是,试错法。但是考虑一个双峰图像(简单来说,双峰图像是一个直方图有两个峰值的图像)。对于那个图像,我们可以近似地取这些峰值中间的一个值作为阈值,对吗?这就是 Otsu 二值化所做的。所以简单来说,它会自动从双峰图像的图像直方图中计算出阈值。(对于非双峰图像,二值化将不准确。)

为此,我们使用了 cv2.threshold 函数,但传递了一个额外的符号 cv2.THRESH_OTSU 。对于阈值,只需传入零。然后,该算法找到最佳阈值,并作为第二个输出返回 retval。如果不使用 otsu 阈值,则 retval 与你使用的阈值相同。

查看下面的示例。输入图像是噪声图像。在第一种情况下,我应用了值为 127 的全局阈值。在第二种情况下,我直接应用 otsu 阈值。在第三种情况下,我使用 5x5 高斯核过滤图像以去除噪声,然后应用 otsu 阈值。查看噪声过滤如何改进结果。

** 示例代码:**

# 图像阈值
# Otsu 二值化
import cv2
from matplotlib import pyplot as plt

img = cv2.imread('../image/3.2.2-2.png', 0)
# 全局阈值
ret1, th1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
# Otsu阈值
ret2, th2 = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
# 经过高斯滤波的 Otsu 阈值
blur = cv2.GaussianBlur(img, (5, 5), 0)
ret3, th3 = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
# 画出所有的图像和他们的直方图
images = [img, 0, th1,
          img, 0, th2,
          blur, 0, th3]
titles = ['Original Noisy Image', 'Histogram', 'Global Thresholding (v=127)',
          'Original Noisy Image', 'Histogram', "Otsu's Thresholding",
          'Gaussian filtered Image', 'Histogram', "Otsu's Thresholding"]
for i in range(3):
    plt.subplot(3, 3, i * 3 + 1), plt.imshow(images[i * 3], 'gray')
    plt.title(titles[i * 3]), plt.xticks([]), plt.yticks([])
    plt.subplot(3, 3, i * 3 + 2), plt.hist(images[i * 3].ravel(), 256)
    plt.title(titles[i * 3 + 1]), plt.xticks([]), plt.yticks([])
    plt.subplot(3, 3, i * 3 + 3), plt.imshow(images[i * 3 + 2], 'gray')
    plt.title(titles[i * 3 + 2]), plt.xticks([]), plt.yticks([])
plt.show()

效果图:
在这里插入图片描述

Otsu 二值化原理

本节演示了 otsu 二值化的 python 实现,以展示它的实际工作原理。如果你不感兴趣,可以跳过这个。

由于我们使用的是双峰图像,因此 Otsu 的算法试图找到一个阈值(t),该阈值将由下式计算得到的类内加权方差最小化:
在这里插入图片描述
其中:
在这里插入图片描述
在这里插入图片描述
数学公式是计算两个分割区间内随机变量的均值(μ1(t),μ2(t))和方差(σ1²(t),σ2²(t))。这里P(i)代表的是从1到I的一个离散概率分布,t为分割点。

  • q1(t) 表示区间 [1, t] 内所有事件的概率之和。
  • q2(t) 表示区间 (t+1, I] 内所有事件的概率之和。
  • μ1(t) 是区间 [1, t] 内随机变量的加权平均数,权重为每个事件发生的概率。
  • μ2(t) 是区间 (t+1, I] 内随机变量的加权平均数,权重也为每个事件发生的概率。
  • σ1²(t) 是区间 [1, t] 内随机变量相对于其均值 μ1(t) 的方差,计算方法为各个事件偏离均值平方后乘以对应概率并求和。
  • σ2²(t) 是区间 (t+1, I] 内随机变量相对于其均值 μ2(t) 的方差,同样按照上述方法计算。

请注意,这些表达式假设了 P(i) 为非负且满足归一化条件:sum_{i=1}^{I} P(i) = 1。同时,在实际应用中,请确保分母 q1(t) 和 q2(t) 非零,以避免除以零的情况发生。

它实际上找到一个 T 值,它位于两个峰值之间,使得两个类的方差最小。它可以简单地在 python 中实现,如下所示:
示例代码:

# 图像阈值
# Otsu 二值化原理
import cv2
import numpy as np

img = cv2.imread('../image/3.1-test.jpg', 0)
blur = cv2.GaussianBlur(img, (5, 5), 0)
# 找到归一化直方图还有累计分布函数
hist = cv2.calcHist([blur], [0], None, [256], [0, 256])
hist_norm = hist.ravel() / hist.max()
Q = hist_norm.cumsum()
bins = np.arange(256)
fn_min = np.inf
thresh = -1
for i in range(1, 256):
    p1, p2 = np.hsplit(hist_norm, [i])  # 概率
    q1, q2 = Q[i], Q[255] - Q[i]  # 类别总和
    b1, b2 = np.hsplit(bins, [i])  # 权重
    # f 找到均值与方差
    m1, m2 = np.sum(p1 * b1) / q1, np.sum(p2 * b2) / q2
    v1, v2 = np.sum(((b1 - m1) ** 2) * p1) / q1, np.sum(((b2 - m2) ** 2) * p2) / q2
    # 计算最小函数
    fn = v1 * q1 + v2 * q2
    if fn < fn_min:
        fn_min = fn
        thresh = i
# 用 OpenCV 函数的 otsu'阈值
ret, otsu = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
print("{} {}".format(thresh, ret))

注意 这其中有很多新的函数,我们会在之后章节讲述。

官方推荐的书

1、Digital Image Processing(数字图像处理), Rafael C. Gonzalez

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

智慧公厕:打造智慧城市公共厕所信息化管理的新升级

在现代社会中&#xff0c;随着科学技术的不断进步与应用&#xff0c;智慧公厕作为公共服务设施&#xff0c;正迎来一次新的升级与革新。利用先进技术&#xff0c;智慧公厕实现了信息化升级&#xff0c;能够实时监测人员、环境和设备状况&#xff0c;提高使用效率、安全性、舒适…

el-table嵌套两层el-dropdown-menu导致样式错乱

问题&#xff1a; 解决方式&#xff1a; <el-table-column label"操作" fixed"right" width"132" align"center"><template slot-scope"scope"><div v-if"scope.row._index ! 合计"><el-d…

超高难度的开关电源PCB设计

开关电源PCB设计 TD芯片的简单吐槽 先介绍下要设计所用的芯片是TD1837和TD1660。   首先先说下一个小的注意事项&#xff0c;一般在设计芯片引脚的布置的时候SW引脚要离FB引脚越远越好。 这里举个TD的反面例子&#xff1a; 图1 引脚示意图 这里吐槽下芯片行业一个不好的点&…

Mac下载Navicat premium提示文件损坏的解决方案

引用&#xff1a;https://blog.csdn.net/weixin_44898291/article/details/120879508 sudo xattr -r -d com.apple.quarantine

使用Cloudreve快速搭建一个私有网盘,配置存储策略和管理权限以及Webdav设置访问

Cloudreve 是什么&#xff1f; Cloudreve 可以让您快速搭建起公私兼备的网盘系统。Cloudreve 在底层支持不同的云存储平台&#xff0c;用户在实际使用时无须关心物理存储方式。你可以使用 Cloudreve 搭建个人用网盘、文件分享系统&#xff0c;亦或是针对大小团体的公有云系统。…

YOLOv8原理与源码解析

课程链接&#xff1a;https://edu.csdn.net/course/detail/39251 【为什么要学习这门课】 Linux创始人Linus Torvalds有一句名言&#xff1a;Talk is cheap. Show me the code. 冗谈不够&#xff0c;放码过来&#xff01;代码阅读是从基础到提高的必由之路。 YOLOv8 基于先前…

kafka简单介绍和代码示例

“这是一篇理论文章&#xff0c;给大家讲一讲kafka” 简介 在大数据领域开发者常常会听到MQ这个术语&#xff0c;该术语便是消息队列的意思&#xff0c; Kafka是分布式的发布—订阅消息系统。它最初由LinkedIn(领英)公司发布&#xff0c;使用Scala语言编写&#xff0c;与2010年…

设计模式-简单工厂

设计模式-简单工厂 简单工厂模式是一个集中管理对象创建&#xff0c;并根据条件生成所需类型对象的设计模式&#xff0c;有助于提高代码的复用性和维护性&#xff0c;但可能会导致工厂类过于复杂且违反开闭原则。 抽象提取理论&#xff1a; 封装对象创建过程解耦客户端与产品…

Kubernetes网络模型概述

Kubernetes网络模型设计的一个基础原则是&#xff1a;每个Pod都拥有一个独立的IP地址&#xff0c;并假定所有Pod都在一个可以直接连通的、扁平的网络空间中。所以不管这些Pod是否运行在同一个Node中&#xff0c;都要求它们可以直接通过对方的IP进行访问。由于Kubernetes的网络模…

基于Docker的Nginx的安装与配置

基于Docker的Nginx的安装与配置 1 为Nginx创建一个容器1.1 学习docker run1.2 通过docker run为Nginx创建并启动一个容器 2 配置Nginx2.1 学习docker的bind mount技术2.2 在Nginx容器中找到想修改的文件所在的目录2.2.1 认识nginx.conf文件2.2.2 访问Nginx服务&#xff0c;默认…

Adobe Acrobat Reader - 老牌PDF编辑器

【应用名称】&#xff1a;Adobe Acrobat Reader - 老牌PDF编辑器 【适用平台】&#xff1a;#Android 【软件标签】&#xff1a;#Adobe 【应用版本】&#xff1a;24.1.0 【应用大小】&#xff1a;482MB 【软件说明】&#xff1a;软件升级更新。用户将有权在手机、平板电脑…

cesium内部相同坐标在不同高度的2个点的属性机制坐标会gltf模型角度值异常问题mars3d的处理办法

模型一直向上运动的正常效果&#xff1a; 问题场景&#xff1a; 1.new mars3d.graphic.ModelPrimitive({使用addDynamicPosition(设置并添加动画轨迹位置&#xff0c;按“指定时间”运动到达“指定位置”时发现&#xff0c;如果是同一个点位不同高度值的y轴竖直向上方向的运动…

yolov8实战第六天——yolov8 TensorRT C++ 部署——(踩坑,平坑,保姆教程)

C 结合 TensorRT 部署深度学习模型有几个关键优势&#xff0c;这些优势在各种工业和商业应用中极其重要&#xff1a; 高效的性能&#xff1a;TensorRT 通过优化深度学习模型来提高推理速度&#xff0c;减少延迟。这对于实时处理应用&#xff08;如视频分析、机器人导航等&#…

老旧小区智慧用电改造方案

【摘要】&#xff1a; 老旧居民小区火灾事故远高于其他场所&#xff0c;而且易造成人员伤亡&#xff0c;随着居民生活水平提高&#xff0c;不断添加各种电气设备&#xff0c;火灾风险逐步加大&#xff0c;智慧用电安全监管平台能够准确全天候地监测线路中的漏电、电流、温度等变…

如何让工业机器视觉呈现更清晰的图像?

清晰度是机器视觉的关键要素&#xff0c;它直接影响后续图像处理和分析的准确性。为了获取更清晰的图像&#xff0c;可以从以下几个方面着手&#xff1a; 1.优化相机设置&#xff1a;曝光时间和增益等参数的调整对图像清晰度有显著影响。通过精确控制这些参数&#xff0c;可以…

【Python】模块

&#x1f6a9; WRITE IN FRONT &#x1f6a9; &#x1f50e; 介绍&#xff1a;"謓泽"正在路上朝着"攻城狮"方向"前进四" &#x1f50e;&#x1f3c5; 荣誉&#xff1a;2021|2022年度博客之星物联网与嵌入式开发TOP5|TOP4、2021|2222年获评…

STC8H8K蓝牙智能巡线小车——1. 环境搭建(基于RTX51操作系统)

1. 基本介绍 开发环境准备&#xff1a;Keil uVision5 烧录软件&#xff1a;STC-ISP&#xff08;V6.92A&#xff09; 芯片&#xff1a; STC8H8K64U-45I-LQFP64 芯片引脚&#xff1a; 2.创建项目 打开Keil&#xff0c;点击【Project】&#xff0c;选择【new uVersion proje…

React入门 - 07(说一说 JSX 中的语法细节)

本章内容 目录 1、js 表达式2、列表渲染3、条件渲染4、className5、jsx 中的样式处理6、dangeouslySetInnerHTML7、htmlFor8、使用 jsx 的注意事项 上一节内容我们完成了一个简单的TodoList案例。到现在为止我们已经知道怎么在 JSX中使用 “js 表达式”和”列表渲染“了&#…

跟随chatgpt学习如何使用GLSL进行简单的图形渲染

1. 准备一个HTML文件&#xff1a;创建一个新的HTML文件&#xff0c;将 HTML 文件命名为 index.html&#xff0c;并添加一个用于显示图形的<canvas>元素。 <!DOCTYPE html> <html> <head><meta charset"utf-8"><title>Simple We…

基于springboot的美食分享平台(程序+数据库+文档)

&#x1f345;点赞收藏关注 → 私信领取本源代码、数据库&#x1f345; 本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目 希望你能有所收获&#xff0c;少走一些弯路。&#x1f345;关注我不迷路&#x1f345;一、研究背景 1.1 课题背景 二…