计算机视觉——OpenCV Otsu阈值法原理及实现
算法简介
Otsu阈值法,也被称为大津算法,是一种在图像处理中广泛使用的自动阈值分割技术。这种方法由日本学者大津展之于1979年提出,旨在根据图像的灰度直方图来自动选择最佳全局阈值。Otsu阈值法的核心思想是最小化类内方差或最大化类间方差。
以下是Otsu阈值法的一般步骤:
-
预处理:对输入图像进行预处理,以减少噪声和增强图像特征。常见的预处理方法包括高斯平滑滤波,这有助于平滑图像,减少随机噪声。
-
灰度直方图:计算图像的灰度直方图,即统计图像中每个灰度级出现的频率。直方图可以提供图像的灰度分布信息。
-
阈值计算:这是Otsu算法的关键步骤。算法通过遍历所有可能的阈值,计算每个阈值对应的类间方差与类内方差之比。Otsu的目标是找到一个阈值,使得这个比值最大化。类内方差最小化意味着阈值两边的像素点尽可能相似,而类间方差最大化意味着阈值两边的像素点差异尽可能大。
-
二值化:使用计算得到的阈值T对原图像进行二值化处理。所有小于或等于阈值T的像素点被设置为背景像素(例如0),而所有大于阈值T的像素点被设置为前景像素(例如255)。
Otsu阈值法的优点在于它的简单性和有效性,特别是在对比度较高的图像中。然而,对于具有复杂背景或光照不均的图像,这种方法可能不够准确。在这些情况下,可能需要更高级的阈值技术或结合其他图像处理技术来获得更好的分割效果。
由于您提供的链接无法解析,如果您需要关于Otsu阈值法的更多信息或有其他相关问题,请告知,我会尽力帮助您。
算法的逻辑
双峰图像(bimodal images)的像素直方图具有两个明显的峰值,这通常意味着图像中存在两种明显不同的像素强度区域,这些区域分别对应于图像中的前景和背景。在这类图像中,前景和背景在灰度或颜色上有明显的区分,因此使用Otsu阈值法可以有效地将它们分开。
双峰直方图的特点是:
-
两个峰值:直方图有两个明显的峰值,分别代表图像中的前景和背景的像素强度分布。
-
低谷:在两个峰值之间存在一个低谷,这个低谷的位置可以作为潜在的阈值,用于区分前景和背景。
-
对比度:两个峰值之间的对比度越高,使用Otsu阈值法的效果通常越好,因为这意味着前景和背景之间的区分度更高。
Otsu算法的核心思想正是利用这种双峰分布的特性,通过最大化类间方差来确定最佳阈值。类间方差衡量的是前景和背景两个类别之间的差异性,而类内方差衡量的是类别内部的一致性。Otsu算法寻找的阈值能够最大化类间方差与类内方差的比值,从而实现最佳的前景和背景分离。
如下就是一个双峰图像的示例:
假设一副灰度图,像素值灰度级为,如我们常见的灰度图像,灰度级是256。
像素值为第个灰度级的像素点有个,则这幅图像总的像素点个数为 N = n 1 + n 2 + . . . n L N=n_1 + n_2 + ...n_L N=n1+n2+...nL
基于上述假设,某个像素点为灰度级的概率可表示为:
p i = n i N p_{i}\,=\,\frac{n_{i}}{N} pi=Nni
满足以下条件:
p i > 0 , ∑ i = 1 L p i = 1 p_{i}\gt 0,\sum_{i=1}^{L}p_{i}=1 pi>0,i=1∑Lpi=1
取灰度级为阈值将这幅图像的像素点分成 C 1 C_1 C1C_2和两簇,
- C 1 C_1 C1包含像素级为[1,2,…,t]的像素
- C 2 C_2 C2包含像素级为[t+1,…,L]的像素
对于图像中某个像素属于 C 1 / C 2 C_1/C_2 C1/C2类的概率可表示为:
ω 1 ( t ) = ∑ i = 1 t p i \omega_{1}(t)=\sum_{i=1}^{t}p_{i} ω1(t)=i=1∑tpi
ω 2 ( t ) = ∑ i = t + 1 L p i \omega_{2}(t)=\sum_{i=t+1}^{L}p_{i} ω2(t)=i=t+1∑Lpi
w 1 ( t ) , w 2 ( t ) w_1(t),w_2(t) w1(t),w2(t),满足关系 w 1 ( t ) = 1 − w 2 ( t ) w_1(t) = 1-w_2(t) w1(t)=1−w2(t)
求每个簇对应的像素均值:
同样可推导:
μ 2 ( t ) = ∑ i = t + 1 L i p i ω 2 ( t ) \mu_{2}(t)=\sum_{i=t+1}^{L}\frac{i p_{i}}{\omega_{2}(t)} μ2(t)=i=t+1∑Lω2(t)ipi
整幅图像的像素均值记为:
μ T = ∑ i L i ∗ p i = ω 1 ( t ) μ 1 ( t ) + ω 2 ( t ) μ 2 ( t ) \mu_{T}=\sum_{i}^{L}i*p_{i}=\omega_{1}(t)\mu_{1}(t)+\omega_{2}(t)\mu_{2}(t) μT=i∑Li∗pi=ω1(t)μ1(t)+ω2(t)μ2(t)
求 C 1 / C 2 C_1/C_2 C1/C2每个簇对应的像素值方差:
σ 1 2 ( t ) = ∑ i = 1 t [ i − μ 1 ( t ) ] 2 ∗ n i ∑ i = 1 t n i = ∑ i = 1 t [ i − μ 1 ( t ) ] 2 ∗ n i N ∑ i = 1 t n i = ∑ i = 1 t [ i − μ 1 ( t ) ] 2 p i ω 1 ( t ) \sigma_{1}^{2}(t)=\frac{\sum_{i=1}^{t}[i-\mu_{1}(t)]^{2}*n_{i}}{\sum_{i=1}^{t}n_{i}}=\frac{\sum_{i=1}^{t}\frac{[i-\mu_{1}(t)]^{2}*n_{i}}{N}}{\sum_{i=1}^{t}n_{i}}=\frac{\sum_{i=1}^{t}[i-\mu_{1}(t)]^{2}p_{i}}{\omega_{1}(t)} σ12(t)=∑i=1tni∑i=1t[i−μ1(t)]2∗ni=∑i=1tni∑i=1tN[i−μ1(t)]2∗ni=ω1(t)∑i=1t[i−μ1(t)]2pi
同样可推导:
σ 2 2 ( t ) = ∑ i = t + 1 L [ i − μ 2 ( t ) ] 2 p i ω 2 ( t ) \sigma_{2}^{2}(t)=\frac{\sum_{i=t+1}^{L}[i-\mu_{2}(t)]^{2}p_{i}}{\omega_{2}(t)} σ22(t)=ω2(t)∑i=t+1L[i−μ2(t)]2pi
为了衡量所取阈值的二值化效果,作者定义了三种方差,分别是:
类内方差:
σ W 2 = ω 1 σ 1 2 + ω 2 σ 2 2 \sigma_{W}^{2}=\omega_{1}\sigma_{1}^{2}+\omega_{2}\sigma_{2}^{2} σW2=ω1σ12+ω2σ22
类间方差:
σ B 2 = ω 1 ( μ 1 − μ T ) 2 + ω 2 ( μ 2 − μ T ) 2 = ω 1 ω 2 ( μ 1 − μ 2 ) 2 \sigma_{B}^{2}=\omega_{1}(\mu_{1}-\mu_{T})^{2}+\omega_{2}(\mu_{2}-\mu_{T})^{2}=\omega_{1}\omega_{2}(\mu_{1}-\mu_{2})^{2} σB2=ω1(μ1−μT)2+ω2(μ2−μT)2=ω1ω2(μ1−μ2)2
图像总的像素值方差:
σ T 2 = ∑ i = 1 L ( i − μ T ) 2 p i \sigma_{T}^{2}=\sum_{i=1}^{L}(i-\mu_{T})^{2}p_{i} σT2=i=1∑L(i−μT)2pi
可以推导三者之间有如下关系:
σ W 2 + σ B 2 = σ T 2 \sigma_{W}^{2}+\sigma_{B}^{2}=\sigma_{T}^{2} σW2+σB2=σT2
从上面的定义可以发现 σ W 2 / σ B 2 \sigma_{W}^{2}/\sigma_{B}^{2} σW2/σB2,于阈值t有关,而 σ T 2 \sigma_{T}^{2} σT2与阈值无关。
上面是的二阶函数,是的一阶函数,更易优化。最后,求阈值可以变成最大化类间方差
σ B 2 ( t ∗ ) = max 1 ≤ t ≤ L σ B 2 ( t ) \sigma_{B}^{2}(t^{*})=\operatorname*{max}_{1\le t\le L}\sigma_{B}^{2}(t) σB2(t∗)=1≤t≤LmaxσB2(t)
C++ 源码实现
// Include Libraries
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc.hpp>using namespace std;
using namespace cv;int main() {// Read the image in grayscale formatMat testImage = imread("boat.jpg", IMREAD_GRAYSCALE);int bins_num = 256;// Get the histogramlong double histogram[256];// Initialize all intensity values to 0for (int i = 0; i < bins_num; i++) {histogram[i] = 0;}// Calculate the number of pixels for each intensity valuefor (int y = 0; y < testImage.rows; y++) {for (int x = 0; x < testImage.cols; x++) {histogram[(int)testImage.at<uchar>(y, x)]++;}}// Calculate bin edges and bin midslong double bin_edges[256];bin_edges[0] = 0.0;long double increment = 0.99609375;for (int i = 1; i < bins_num; i++) {bin_edges[i] = bin_edges[i - 1] + increment;}long double bin_mids[256];for (int i = 0; i < bins_num; i++) {bin_mids[i] = (bin_edges[i] + bin_edges[i + 1]) / 2;}// Calculate weights for each classlong double weight1[256];weight1[0] = histogram[0];for (int i = 1; i < bins_num; i++) {weight1[i] = histogram[i] + weight1[i - 1];}int total_sum = 0;for (int i = 0; i < bins_num; i++) {total_sum += histogram[i];}long double weight2[256];weight2[0] = total_sum;for (int i = 1; i < bins_num; i++) {weight2[i] = weight2[i - 1] - histogram[i - 1];}// Calculate class meanslong double histogram_bin_mids[256];for (int i = 0; i < bins_num; i++) {histogram_bin_mids[i] = histogram[i] * bin_mids[i];}long double cumsum_mean1[256];cumsum_mean1[0] = histogram_bin_mids[0];for (int i = 1; i < bins_num; i++) {cumsum_mean1[i] = cumsum_mean1[i - 1] + histogram_bin_mids[i];}long double cumsum_mean2[256];cumsum_mean2[0] = histogram_bin_mids[255];for (int i = 1, j = bins_num - 1; i < bins_num; i++, j--) {cumsum_mean2[i] = cumsum_mean2[i - 1] + histogram_bin_mids[j];}long double mean1[256];for (int i = 0; i < bins_num; i++) {mean1[i] = cumsum_mean1[i] / weight1[i];}long double mean2[256];for (int i = 0, j = bins_num - 1; i < bins_num; i++, j--) {mean2[j] = cumsum_mean2[i] / weight2[j];}// Calculate Inter_class_variancelong double Inter_class_variance[255];long double dnum = 10000000000.0; // Scaling factor to avoid overflowfor (int i = 0; i < 255; i++) {Inter_class_variance[i] = ((weight1[i] * weight2[i] * (mean1[i] - mean2[i + 1])) / dnum) * (mean1[i] - mean2[i + 1]);}// Maximize interclass variance to find the thresholdlong double maxi = 0;int getmax = 0;for (int i = 0; i < 255; i++) {if (maxi < Inter_class_variance[i]) {maxi = Inter_class_variance[i];getmax = i;}}cout << "Otsu's algorithm implementation thresholding result: " << bin_mids[getmax] << endl;return 0;
}
Python 代码实现
import cv2
import numpy as npdef otsu_thresholding(image_path):# 读取图像image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)if image is None:print("Error: 图像未找到.")return# 获取直方图hist = cv2.calcHist([image], [0], None, [256], [0, 256])# 计算总像素total_pixels = image.size# 初始化类间方差inter_class_variance = 0# 初始化权重和w0 = w1 = 0# 初始化类内总和sum0 = np.sum(image[image < inter_class_variance])sum1 = np.sum(image[image > inter_class_variance])# 初始化类内平方和var0 = np.sum((image[image < inter_class_variance] - sum0 / w0) ** 2)var1 = np.sum((image[image > inter_class_variance] - sum1 / w1) ** 2)# 寻找最佳阈值max_variance = 0threshold = 0for threshold in range(1, 256):w0 += hist[threshold - 1]w1 = total_pixels - w0sum0 += threshold * hist[threshold - 1]sum1 -= threshold * hist[threshold - 1]var0 = w0 / (w0 + w1) * np.sum((image[image <= threshold] - (sum0 / w0)) ** 2)var1 = w1 / (w0 + w1) * np.sum((image[image > threshold] - (sum1 / w1)) ** 2)inter_class_variance = var0 + var1if inter_class_variance > max_variance:max_variance = inter_class_variancethreshold = threshold# 使用最佳阈值二值化图像_, binary_image = cv2.threshold(image, threshold, 255, cv2.THRESH_BINARY)return binary_image, threshold# 使用函数
image_path = 'boat.jpg' # 请确保路径正确
binary_image, threshold = otsu_thresholding(image_path)# 显示结果
cv2.imshow('Original Image', cv2.imread(image_path, cv2.IMREAD_GRAYSCALE))
cv2.imshow('Binary Image', binary_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
相关文章:

计算机视觉——OpenCV Otsu阈值法原理及实现
算法简介 Otsu阈值法,也被称为大津算法,是一种在图像处理中广泛使用的自动阈值分割技术。这种方法由日本学者大津展之于1979年提出,旨在根据图像的灰度直方图来自动选择最佳全局阈值。Otsu阈值法的核心思想是最小化类内方差或最大化类间方差…...

个人IP打造孵化运营产业链商业计划书
【干货资料持续更新,以防走丢】 个人IP打造孵化运营产业链商业计划书 部分资料预览 资料部分是网络整理,仅供学习参考。 PPT共90页(完整资料包含以下内容) 目录 个人IP运营方案: 1. 个人IP定位与构建 1.1 人格画像构…...
R语言:卡方检验
χ2检验(Chi-Square Test)是一种用于检验分类变量之间是否存在相关性的统计方法。χ2检验的原理基于观察到的频数与期望频数之间的偏差来判断分类变量之间是否存在显著的关联。 χ2检验的原理可以概括为以下几个步骤: 建立假…...

基于51单片机的电子钟秒表LCD1602仿真设计( proteus仿真+程序+设计报告+原理图+讲解视频)
基于51单片机的电子钟秒表LCD1602仿真设计( proteus仿真程序设计报告原理图讲解视频) 这里写目录标题 1. 主要功能:2. 讲解视频:3. 仿真4. 程序代码5. 设计报告6. 原理图7. 设计资料内容清单&&下载链接 仿真图proteus7.8及以上 程序…...

latex参考文献引用网址,不显示网址问题
以引用UCI数据集为例 1、加入宏包 \usepackage{url} 2、在参考文献bib文件中加入网址文献 misc{UCI, author {{D. Dua, E. Karra Taniskidou}}, year {2024}, title {UCI Machine Learning Repository}, howpublished {\url{http://archive.ics.uci.edu/ml}} } 完成&#x…...

详细分析Mybatis与MybatisPlus中分页查询的差异(附Demo)
目录 前言1. Mybatis2. MybatisPlus3. 实战 前言 更多的知识点推荐阅读: 【Java项目】实战CRUD的功能整理(持续更新)java框架 零基础从入门到精通的学习路线 附开源项目面经等(超全) 本章节主要以Demo为例ÿ…...

nginx--tcp负载均衡
mysql负载均衡 安装mysql yum install -y mariadb-server systemctl start mariadb systemctl enable mariadb ss -ntl创建数据库并授权 MariaDB [(none)]> create database wordpress; Query OK, 1 row affected (0.00 sec)MariaDB [(none)]> grant all privileges o…...

20240506 深度学习高级技术点
1.基于BN层剪枝 基于Batch Normalization (BN)层进行剪枝是一种常用的模型压缩方法,特别是在卷积神经网络(CNNs)中。BN层在训练期间用于加速收敛和提高模型的泛化能力,而在剪枝过程中,BN层提供的统计信息(特别是均值(mean)和方差…...

标准汽车试验铁地板的技术要求
在现代科技化发展的工作中,试验平台被广泛使用。铸铁试验平台(试验铁地板)又叫试验工作平台,听名字大家也不难想象出来这是一款带有箱式体的台面,这是一种有长方形或者圆形又或者正方形的试验工作台。 铸铁试验平台&a…...

【翻译】REST API
自动伸缩 API 创建或更新自动伸缩策略 API 此特性设计用于 Elasticsearch Service、Elastic Cloud Enterprise 和 Kubernetes 上的 Elastic Cloud 的间接使用。不支持直接用户使用。 创建或更新一个自动伸缩策略。 请求 PUT /_autoscaling/policy/<name> {"rol…...

TypeScript学习日志-第十九天(namespace命名空间)
namespace命名空间 一、基本用法 namespace 所有的变量以及方法必须要导出才能访问,如图: 二、 嵌套 namespace 可以进行嵌套使用,如图: 它也必须需要导出才能访问 三、合并 当我们出现两个同名的 namespace 它就会合并这两…...
Mac远程桌面windows11
文章目录 一、Mac安装Microsoft Remote Desktop二、Window11打开远程登录权限三、常见问题 一、Mac安装Microsoft Remote Desktop 下载地址:点击这里官方下载 基本不用看,凭感觉用就行。搞不定详情见:官方说明 二、Window11打开远程登录权…...

GPU术语
SP(Streaming Processor)流处理器 流处理器是GPU最基本的处理单元,在fermi架构开始被叫做CUDA core。 SM(Streaming MultiProcessor) 一个SM由多个CUDA core组成。SM还包括特殊运算单元(SFU),共享内存(shared memory),寄存器文件(Register …...

上海个人购房提取公积金经历和注意事项(收藏不踩坑)
在前一篇文章中,我介绍了 2024 年 4 月 24 日上海个人购房个税退税经历,我于 4 月 27 日周六,顺利办理租房公积金提取业务,资金在业务办理完成后 10 分钟左右到账。通过本文分享办理过程的材料和注意事项,避免大家踩坑…...

阿里云API网关 产品的使用笔记
阿里云的产品虽多,还是一如既往的一用一个看不懂,该模块的文档依旧保持“稳定”发挥,磕了半天才全部跑通。 用阿里云API网关的原因是,在Agent中写插件调用API的时候,需要使用Https协议,又嫌搞备案、证书等事…...

基于H.264的RTP打包中的组合封包以及分片封包结构图简介及抓包分析;FU-A FU-B STAP-A STAP-B简介;
H.264视频流的RTP封装类型分析: 前言: 1.RTP打包原则: RTP的包长度必须要小于MTU(最大传输单元),IP协议中MTU的最大长度为1500字节。除去IP报头(20字节)、UDP报头(8字节)、RTP头&a…...
OPENAI中Semantic Kernel实现原理以及示例代码用PYTHON来实现
OPENAI中Semantic Kernel实现原理以及示例代码用PYTHON来实现 前言 在人工智能领域,自然语言处理是一个非常重要的研究方向。而在自然语言处理中,语义理解是一个非常关键的问题。在这个领域中,OPENAI的Semantic Kernel是一个非常有名的工具…...
关于路由懒加载的实现
在Vue2中,实现路由懒加载可以使用import的动态引入方式。通常,我们可以将组件作为被引入的模块,并在routes配置中使用component: () > import(/components/Example.vue)来实现懒加载。 在Vue3中,懒加载的实现方式稍有不同。Vu…...

如何去官网下载windows10操作系统iso镜像
文章目录 一、先从微软中国官网https://www.microsoft.com/zh-cn/进去二、然后按图示一步步点进去三、点击下载工具这个工具会帮你生成windows操作系统iso文件四、下载好后一步步按图示要求成功操作 一、先从微软中国官网https://www.microsoft.com/zh-cn/进去 二、然后按图示一…...
JavaScript中如何实现函数缓存,函数缓存有哪些应用场景
函数缓存就是将函数运算的结果进行缓存。 本质上是利用空间换时间。 常用于缓存数据计算结果和缓存对象。缓存只是一个临时的数据存储,它保存数据,为了方便将来对该数据的请求时,可以更快的得到处理。 缓存函数 实现一个缓存函数…...

大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...

大型活动交通拥堵治理的视觉算法应用
大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...

自然语言处理——循环神经网络
自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元(GRU)长短期记忆神经网络(LSTM)…...

2025季度云服务器排行榜
在全球云服务器市场,各厂商的排名和地位并非一成不变,而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势,对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析: 一、全球“三巨头”…...

嵌入式学习笔记DAY33(网络编程——TCP)
一、网络架构 C/S (client/server 客户端/服务器):由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序,负责提供用户界面和交互逻辑 ,接收用户输入,向服务器发送请求,并展示服务…...

ubuntu系统文件误删(/lib/x86_64-linux-gnu/libc.so.6)修复方案 [成功解决]
报错信息:libc.so.6: cannot open shared object file: No such file or directory: #ls, ln, sudo...命令都不能用 error while loading shared libraries: libc.so.6: cannot open shared object file: No such file or directory重启后报错信息&…...
在golang中如何将已安装的依赖降级处理,比如:将 go-ansible/v2@v2.2.0 更换为 go-ansible/@v1.1.7
在 Go 项目中降级 go-ansible 从 v2.2.0 到 v1.1.7 具体步骤: 第一步: 修改 go.mod 文件 // 原 v2 版本声明 require github.com/apenella/go-ansible/v2 v2.2.0 替换为: // 改为 v…...

数据挖掘是什么?数据挖掘技术有哪些?
目录 一、数据挖掘是什么 二、常见的数据挖掘技术 1. 关联规则挖掘 2. 分类算法 3. 聚类分析 4. 回归分析 三、数据挖掘的应用领域 1. 商业领域 2. 医疗领域 3. 金融领域 4. 其他领域 四、数据挖掘面临的挑战和未来趋势 1. 面临的挑战 2. 未来趋势 五、总结 数据…...

虚拟机网络不通的问题(这里以win10的问题为主,模式NAT)
当我们网关配置好了,DNS也配置好了,最后在虚拟机里还是无法访问百度的网址。 第一种情况: 我们先考虑一下,网关的IP是否和虚拟机编辑器里的IP一样不,如果不一样需要更改一下,因为我们访问百度需要从物理机…...
Python爬虫(四):PyQuery 框架
PyQuery 框架详解与对比 BeautifulSoup 第一部分:PyQuery 框架介绍 1. PyQuery 是什么? PyQuery 是一个 Python 的 HTML/XML 解析库,它采用了 jQuery 的语法风格,让开发者能够用类似前端 jQuery 的方式处理文档解析。它的核心特…...