CV笔记5:图像分割之最大类间方差法、自适应阈值分割(基于python-opencv实现)

article/2025/8/7 19:11:49

目录

一、简介

二、最大类间方差法(大津法)

2.1 最大类间方差法原理

2.2 基于opencv的实现(简单阈值分割、Otsu阈值分割)

三、自适应阈值分割

参考


一、简介

图像阈值化分割是一种传统的最常用的图像分割方法,因其实现简单、计算量小、性能较稳定而成为图像分割中最基本和应用最广泛的分割技术。它特别适用于目标和背景占据不同灰度级范围的图像。它不仅可以极大的压缩数据量,而且也大大简化了分析和处理步骤,因此在很多情况下,是进行图像分析、特征提取与模式识别之前的必要的图像预处理过程。图像阈值化的目的是要按照灰度级,对像素集合进行一个划分,得到的每个子集形成一个与现实景物相对应的区域,各个区域内部具有一致的属性,而相邻区域不具有这种一致属性。这样的划分可以通过从灰度级出发选取一个或多个阈值来实现。

二、最大类间方差法(大津法)

大津法又叫最大类间方差法、最大类间阈值法(OTSU)。它的基本思想是,用一个阈值将图像中的数据分为两类,一类中图像的像素点的灰度均小于这个阈值,另一类中的图像的像素点的灰度均大于或者等于该阈值。如果这两个类中像素点的灰度的方差越大,说明获取到的阈值就是最佳的阈值(方差是灰度分布均匀性的一种度量,背景和前景之间的类间方差越大,说明构成图像的两部分的差别越大,当部分前景错分为背景或部分背景错分为前景都会导致两部分差别变小,因此,使类间方差最大的分割意味着错分概率最小)。利用该阈值可以将图像分为前景和背景两个部分,而我们所感兴趣的部分一般为前景。通过上述,我们可以看出,大津法是一种全局阈值法。

大津法计算简单快速,不受图像亮度和对比度的影响。但对图像噪声敏感;只能针对单一目标分割;当目标和背景大小比例悬殊、类间方差函数可能呈现双峰或者多峰,这个时候效果不好。

2.1 最大类间方差法原理

对于图像I(x,y),前景(即目标)和背景的分割阈值记作T,属于前景的像素点数占整幅图像的比例记为ω0,其平均灰度μ0;背景像素点数占整幅图像的比例为ω1,其平均灰度为μ1。图像的总平均灰度记为μ,类间方差记为σ2。

假设图像的背景较暗,并且图像的大小为M×N,图像中像素的灰度值小于阈值T的像素个数记作N0,像素灰度大于阈值T的像素个数记作N1,则有:

将公式(5)代入式(6),得到等价公式为:

上式就是求得的类间方差,再通过遍历,对图片的像素级进行遍历,找到使类间方差最大的阈值T,即为所求的分割阈值。

根据上面的公式,我们可以做进一步的变形:

根据定义我们能够进一步推导μ0和μ1的变形公式如下:

式中,L表示图像灰度级,ni表示灰度为i的像素的数量。

考虑灰度级T的累加均值m(非完全严格定义)和图像的全局均值μ为:

根据公式(10)和(11)可以将公式(8)和(9)改写成:

将公式(12)(13)代入式(7),可得:

 

2.2 基于opencv的实现(简单阈值分割、Otsu阈值分割)

简单阈值分割

在opencv中可以使用cv2.threshold()函数进行简单阈值分割。

这个函数的第一个参数就是原图像,原图像应该是灰度图。第二个参数就是用来对像素值进行分类的阀值,第三个参数就是当像素值高于(或者小于)阀值时,应该被赋予新的像素值。OpenCV提供了多种不同的阀值方法,这是有第四个参数来决定的。方法包括:

cv2.THRESH_BINARY 二值阈值化

cv2.THRESH_BINARY_INV 反向二值阈值化

cv2.THRESH_TRUNC 截断阈值化

cv2.THRESH_TOZERO 超过阈值归零

cv2.THRESH_TOZERO_INV 低于阈值归零

该方法返回两个输出。第一个是使用的阈值,第二个输出是阈值后的图像

简单阈值分割代码实现:

import cv2 as cv
import numpy as np
from matplotlib import pyplot as pltimg = cv.imread('gradient.png',0)ret,thresh1 = cv.threshold(img,127,255,cv.THRESH_BINARY)
ret,thresh2 = cv.threshold(img,127,255,cv.THRESH_BINARY_INV)
ret,thresh3 = cv.threshold(img,127,255,cv.THRESH_TRUNC)
ret,thresh4 = cv.threshold(img,127,255,cv.THRESH_TOZERO)
ret,thresh5 = cv.threshold(img,127,255,cv.THRESH_TOZERO_INV)titles = ['Original Image','BINARY','BINARY_INV','TRUNC','TOZERO','TOZERO_INV']
images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]
for i in xrange(6):plt.subplot(2,3,i+1),plt.imshow(images[i],'gray')plt.title(titles[i])plt.xticks([]),plt.yticks([])
plt.show()

简单阈值分割效果:

Otsu阈值分割

正如上面的实例,在全局阈值化中,我们使用任意选择的值作为阈值。而Otsu的方法避免了必须选择一个值并能够自动确定阈值。在cv2.threshold()函数中因为Otsu’s方法会产生一个阈值,那么函数cv2.threshold的的第二个参数(设置阈值)就是0了,并且在cv2.threshold的方法参数中还得加上语句cv2.THRESH_OTSU。

考虑仅具有两个不同图像值的图像(双峰图像),其中直方图将仅包含两个峰。一个好的阈值应该在这两个值的中间。类似地,Otsu的方法从图像直方图中确定最佳全局阈值。

查看以下示例。输入图像为噪点图像。在第一种情况下,采用值为127的全局阈值。在第二种情况下,直接采用Otsu阈值法。在第三种情况下,首先使用5x5高斯核对图像进行滤波以去除噪声,然后应用Otsu阈值处理。了解噪声滤波如何改善结果。

Otsu阈值分割代码实现:

import cv2 as cv
import numpy as np
from matplotlib import pyplot as pltimg = cv.imread('noisy2.png',0)# 全局阈值
ret1,th1 = cv.threshold(img,127,255,cv.THRESH_BINARY)
# Otsu阈值
ret2,th2 = cv.threshold(img,0,255,cv.THRESH_BINARY+cv.THRESH_OTSU)
# 高斯滤波后再采用Otsu阈值
blur = cv.GaussianBlur(img,(5,5),0)
ret3,th3 = cv.threshold(blur,0,255,cv.THRESH_BINARY+cv.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 xrange(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算法,但这算法属于全局阈值法,所以对于某些光照不均的图像,这种全局阈值分割的方法会显得苍白无力,如下图情况:

我们可以通过自适应阈值法(adaptiveThreshold)改变这种情况。它的思想不是计算全局图像的阈值,而是根据图像不同区域亮度分布,计算其局部阈值,所以对于图像不同区域,能够自适应计算不同的阈值,因此被称为自适应阈值法(其实就是局部阈值法)。我们可以通过计算某个邻域(局部)的均值、中值、高斯加权平均(高斯滤波)来确定阈值,从而实现自适应阈值分割。值得说明的是:如果用局部的均值作为局部的阈值,就是常说的移动平均法。

在opencv中提供了cv2.adaptiveThreshold()实现自适应阈值分割。

adaptiveThreshold(src: Any,             #源图像maxValue: Any,        #分配给超过阈值的像素值的最大值adaptiveMethod: Any,  #在一个邻域内计算阈值所采用的算法,有两个取值,分别为 ADAPTIVE_THRESH_MEAN_C 和 ADAPTIVE_THRESH_GAUSSIAN_C thresholdType: Any,   #这是阈值类型,只有两个取值,分别为 THRESH_BINARY 和THRESH_BINARY_INVblockSize: Any,       #adaptiveThreshold的计算单位是像素的邻域块,这是局部邻域大小C: Any,               #这个参数实际上是一个偏移值调整量,用均值和高斯计算阈值后,再减或加这个值就是最终阈值dst: Any = None)      #输出图像

自适应阈值分割代码实现:

import cv2
import matplotlib.pyplot as pltliteracy_path = 'imageData/literacy.jpg'img = cv2.imread(literacy_path, 0)# threshold
ret, thresh = cv2.threshold(img_literacy, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
print(ret)  # 124
# adaptive threshold
thresh1 = cv2.adaptiveThreshold(img_literacy, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 0)
thresh2 = cv2.adaptiveThreshold(img_literacy, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 2)
thresh3 = cv2.adaptiveThreshold(img_literacy, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 0)
thresh4 = cv2.adaptiveThreshold(img_literacy, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2)# show image
plt.figure('adaptive threshold', figsize=(12, 8))
plt.subplot(231), plt.imshow(img_literacy, cmap='gray'), plt.title('original')
plt.subplot(234), plt.imshow(thresh, cmap='gray'), plt.title('otsu')
plt.subplot(232), plt.imshow(thresh1, cmap='gray'), plt.title('adaptive_mean_0')
plt.subplot(235), plt.imshow(thresh2, cmap='gray'), plt.title('adaptive_mean_2')
plt.subplot(233), plt.imshow(thresh3, cmap='gray'), plt.title('adaptive_gaussian_0')
plt.subplot(236), plt.imshow(thresh4, cmap='gray'), plt.title('adaptive_gaussian_2')
plt.show()

自适应阈值分割效果:

 

 

参考

http://www.woshicver.com/FifthSection/4_3_%E5%9B%BE%E5%83%8F%E9%98%88%E5%80%BC/

https://www.kancloud.cn/aollo/aolloopencv/267591

自适应阈值(adaptiveThreshold)分割原理及实现

 


http://chatgpt.dhexx.cn/article/ImDMfmi8.shtml

相关文章

图像处理之:最大类间方差法法进行图像二值化处理

方法简介: gω0ω1(μ0-μ1)^2 对于图像I(x,y),前景(即目标)和背景的分割阈值记作T,属于前景的像素点数占整幅图像的比例记为ω0,其平均灰度μ0; 背景像素点数占整幅图像的比例为ω1,其平均灰度为μ1,类间方差记为g。采用遍历的方法得到使类间方差最大的阈值T int…

学习笔记———《最大类间方差法》

最大类间方差法是由日本学者大津(N.Otsu)于1979年提出的,又叫大津法,简称Otsu法,是一种自适应的阈值确定的方法。 它的基本思想是以某一灰度值(如t)为阈值,将图像中的像素分成两类C0和C1,然后计算它们的方差&#xff0…

自动选择阈值--最大类间方差法(OTSU)原理及matlab实现

原理 基本思想: 把一幅图像分成两部分C0,C1.使前景和背景与平均灰度的差别最大。这种差异用方差sigmaB表示。 所以目标就是求最大方差sigmaB对应的阈值T。 matlab代码 clc clear ximread(rice.png); ax;subplot(211); imshow(a,[]); %[count x]imhist(…

【机器视觉学习笔记】大津法/Otsu最大类间方差法 最佳阈值处理(C++)

目录 概念C源码OtsuThreshold主函数 效果完整源码 平台:Windows 10 20H2 Visual Studio 2015 OpenCV 4.5.3 本文所用源码修改自C opencv 图片二值化最佳阈值确定(大津法,OTSU算法)——Sharon Liu 概念 Otsu算法,也叫最大类间方差法&#x…

使用matlab编程实现基于阈值分割的扫描文档二值化方法,MATLAB实现基于最大类间方差法的图像分割算法...

clear clc; %% 选择图片,并二值化 [fn,pn,fi]uigetfile(*.jpg,选择图片); Iimread([pn fn]); if ndims(I) 3 I rgb2gray(I); end % fxy imhist(I, 256); %统计每个灰度值的个数 [counts,x] imhist(I, 256) ; figure; subplot(2, 2, 1); imshow(I, []); title(原…

基于遗传算法的二维最大类间方差法的图像分割优化

一、背景 最大类间方差阈值分割法日本大津展之在1980年提出的,其基本思路是将图像的直方图以某一灰度为阈值,将图像分成两组并计算两组的方差,当被分成的两组之间的方差最大时,就以这个灰度值为國值分割图像。 遗传算法就是借鉴自…

数字图像处理——最大类间方差法(OTSU)图像阈值分割实例

数字图像处理——最大类间方差法(OTSU)图像阈值分割实例 数字图像处理——最大类间方差法(OTSU)图像阈值分割实例 图像阈值分割是指通过以某个确定的图像灰度值(灰度级)将图像分为不同的部分,…

【图像分割】最大类间方差法(otsu)图像分割

由Otsu(大津展之)于1978年提出的最大类间方差法,是引起较多关注的一种阈值选取方法。它是在判决分析或最小二乘原理的基础上推导出来的。 参考文献: [1] Otsu N. A threshold selection method from gray-level histogram. IEEE Trans,1979;SMC-9;62-66 …

最大类间方差法Ostu的C++实现

① 设灰度级为L,灰度值i的像素出现的频数为。分别为所纷呈的两个像素类的面积比,即 ② 分别为两个像素类的平均灰度值,即 ③计算类间方差,并找出最大类间方差以及对应的阈值。 ④ 求为最大值时的t,作为图像分割的最…

图像分割 - 阈值处理 - 最大类间方差法(OTSU)

目录 1. OTSU 介绍 2. 代码实现 1. OTSU 介绍 OTSU 大津法&#xff0c;也是最大类间方差算法 OTSU 算法的思想通过不同的阈值K&#xff0c;将图像的分为两个区域&#xff0c;一个是灰度值 0 < k < K 的子区域G1&#xff1b;另一个是灰度值 K1 < k < L-1 的子区…

【零散知识】最大类间方差法(大津法,Otsu)

前言&#xff1a; { 最近特别忙&#xff08;或者说时间规划出了问题&#xff09;&#xff0c;所以更新的都是短篇。 本次的内容是最大类间方差法&#xff08;大津法&#xff0c;Otsu&#xff09;。 } 正文&#xff1a; { 根据[1]中的介绍&#xff0c;大津法的主要作用是二值…

「 Redis 」 SkipList 跳表底层实现及应用

「 Redis 」 SkipList 跳表底层实现及应用 参考&鸣谢 Redis中ZSet的底层数据结构跳跃表skiplist&#xff0c;你真的了解吗&#xff1f; RiemannChow 深入理解跳表及其在Redis中的应用 京东云开发者 Redis跳表底层实现 来年花惜 文章目录 「 Redis 」 SkipList 跳表底层实现…

Redis 中的底层数据结构:SkipList

一、SkipList 简介 SkipList(5.0) 是zset的底层实现之一&#xff0c;它的数据结构定义如下&#xff1a; /* ZSETs use a specialized version of Skiplists */ typedef struct zskiplistNode {sds ele; //成员对象double score; //分值struct zskiplistNode *backward; //后退…

skiplist原理与实现

今天继续介绍分布式系统当中常用的数据结构&#xff0c;今天要介绍的数据结构非常了不起&#xff0c;和之前介绍的布隆过滤器一样&#xff0c;是一个功能强大原理简单的数据结构。并且它的缺点和短板更少&#xff0c;应用更加广泛&#xff0c;比如广泛使用的Redis就有用到它。 …

【数据结构】跳表Skiplist

文章目录 跳表--skiplistskiplist的概念skilplist的原理skilplist的实现随机值函数跳表节点 跳表框架查找函数寻找前置节点添加元素删除元素打印链表 测试结果Skiplist与其他Key-Value结构的比较 跳表–skiplist skiplist的概念 skiplist本质上也是一种查找结构&#xff0c;用…

Skiplist跳表详解及其模拟实现

文章目录 跳表1.跳表的概念2.Skiplist在插入时采用随机层数的方法是如何保证效率的呢?3.跳表的模拟实现4.跳表VS平衡搜索树和哈希表 跳表 1.跳表的概念 跳表是基于有序链表扩展实现的。对于一个普通的有序链表&#xff0c;我们查找数据的时间复杂度是O(N)。而跳表的出现&…

skiplist - 跳表

一 前言 跳表(skiplist、跳跃表) 是一个很优秀的数据结构&#xff0c;比如用于 Redis、levelDB等出名的开源项目上。跳表在原有的有序链表上面增加了多级索引&#xff0c;通过索引来实现快速查找。 它的结构特点在名称能很好的体现出来&#xff0c;就像兔子一样&#xff0c;蹦…

Leveldb skiplist 实现及解析

skiplist 原理介绍 skiplist 由William Pugh 在论文Skip Lists: A Probabilistic Alternative to Balanced Trees 中提出的一种数据结构,skiplist 是一种随机化存储的多层线性链表结构,插入,查找,删除的都是对数级别的时间复杂度。skiplist 和平衡树有相同的时间复杂度,但…

跳表SkipList介绍与实现

目录 一.跳表介绍 二.实现思路 &#xff08;一&#xff09;.结点结构 &#xff08;二&#xff09;.检索 &#xff08;三&#xff09;.插入 &#xff08;四&#xff09;.删除 三.实现代码 一.跳表介绍 跳表是一种随机化数据结构&#xff0c;主要用于快速检索数据。实质上…

SkipList(跳表)

SkipList(跳表) 文章目录 SkipList(跳表)参考前言跳表的原理跳表的插入和删除插入操作删除操作 跳表的时间空间复杂度分析时间复杂度空间复杂度 调表的基本操作插入数据删除数据 Go 实现小结 参考 https://juejin.cn/post/6844903955831619597#heading-2https://blog.csdn.net…