OpenCV阈值分割技术:全局阈值与自适应阈值

目录

一、阈值分割的基本概念

阈值分割的基本原理

二、全局阈值分割

[1. cv2.threshold()函数](#1. cv2.threshold()函数)

[2. 不同阈值类型的效果对比](#2. 不同阈值类型的效果对比)

[3. 手动选择阈值](#3. 手动选择阈值)

[4. 基于直方图的阈值选择](#4. 基于直方图的阈值选择)

三、自适应阈值分割

[2. 自适应阈值与全局阈值的对比](#2. 自适应阈值与全局阈值的对比)

[3. 自适应阈值参数的影响](#3. 自适应阈值参数的影响)

四、Otsu's自动阈值分割

[1. Otsu's算法的原理](#1. Otsu's算法的原理)

[2. OpenCV中的Otsu's实现](#2. OpenCV中的Otsu's实现)

[3. Otsu's算法与高斯模糊结合](#3. Otsu's算法与高斯模糊结合)

五、全局阈值与自适应阈值的对比

[1. 对比实验](#1. 对比实验)

[2. 优缺点对比](#2. 优缺点对比)

六、实际应用案例

[1. 文档扫描](#1. 文档扫描)

[2. 硬币检测](#2. 硬币检测)

七、阈值分割的技巧与注意事项

[1. 图像预处理](#1. 图像预处理)

[2. 阈值类型的选择](#2. 阈值类型的选择)

[3. 自适应阈值参数调整](#3. 自适应阈值参数调整)

[4. 多阈值分割](#4. 多阈值分割)

八、总结

主要内容回顾


一、阈值分割的基本概念

阈值分割是图像处理中最基本也是最重要的技术之一,它通过将图像中像素的灰度值与一个或多个阈值进行比较,将图像分割为不同的区域(通常是前景和背景)。阈值分割广泛应用于图像预处理、目标检测、图像分割等领域。

阈值分割的基本原理

阈值分割的核心思想是:通过设定一个阈值T,将图像中的像素分为两类:

灰度值大于T的像素属于一类(通常是前景)

灰度值小于等于T的像素属于另一类(通常是背景)

数学表达式:

if f(x,y) > T: g(x,y) = 255 (白色)

else: g(x,y) = 0 (黑色)

二、全局阈值分割

全局阈值分割是指整个图像使用同一个阈值进行分割。在OpenCV中,cv2.threshold()函数用于实现全局阈值分割。

1. cv2.threshold()函数

//python

ret, dst = cv2.threshold(src, thresh, maxval, type)

参数说明:

src:输入图像(必须是灰度图像)

thresh:设定的阈值

maxval:当像素值超过阈值(或小于阈值,根据type而定)时,赋予的新值

type:阈值类型

cv2.THRESH_BINARY:二值化,大于阈值的像素设为maxval,否则设为0

cv2.THRESH_BINARY_INV:反向二值化,小于等于阈值的像素设为maxval,否则设为0

cv2.THRESH_TRUNC:截断,大于阈值的像素设为阈值,否则保持不变

cv2.THRESH_TOZERO:化零,大于阈值的像素保持不变,否则设为0

cv2.THRESH_TOZERO_INV:反向化零,小于等于阈值的像素保持不变,否则设为0

返回值:

ret:实际使用的阈值

dst:阈值分割后的图像

2. 不同阈值类型的效果对比

//python

python 复制代码
import cv2

import numpy as np

from matplotlib import pyplot as plt

 #读取图像
img = cv2.imread('gradient.jpg', 0)

 #设定阈值
thresh_value = 127

maxval = 255

 #应用不同的阈值类型
ret1, thresh_binary = cv2.threshold(img, thresh_value, maxval, cv2.THRESH_BINARY)

ret2, thresh_binary_inv = cv2.threshold(img, thresh_value, maxval, cv2.THRESH_BINARY_INV)

ret3, thresh_trunc = cv2.threshold(img, thresh_value, maxval, cv2.THRESH_TRUNC)

ret4, thresh_tozero = cv2.threshold(img, thresh_value, maxval, cv2.THRESH_TOZERO)

ret5, thresh_tozero_inv = cv2.threshold(img, thresh_value, maxval, cv2.THRESH_TOZERO_INV)

 #显示结果
titles = ['Original Image', 'BINARY', 'BINARY_INV', 'TRUNC', 'TOZERO', 'TOZERO_INV']

images = [img, thresh_binary, thresh_binary_inv, thresh_trunc, thresh_tozero, thresh_tozero_inv]

plt.figure(figsize=(15, 8))

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.tight_layout()

plt.show()

3. 手动选择阈值

手动选择阈值是最基本的方法,适用于图像对比度高、直方图有明显双峰的情况。

//python

python 复制代码
import cv2

import numpy as np

from matplotlib import pyplot as plt

 #读取图像

img = cv2.imread('coins.jpg', 0)

 #手动设定阈值

thresh_value = 130

maxval = 255

 #应用二值化阈值

ret, thresh = cv2.threshold(img, thresh_value, maxval, cv2.THRESH_BINARY_INV)

 #显示结果

plt.figure(figsize=(12, 6))

plt.subplot(121), plt.imshow(img, cmap='gray'), plt.title('Original Image')

plt.subplot(122), plt.imshow(thresh, cmap='gray'), plt.title(f'BINARY_INV (Thresh: {thresh_value})')

plt.tight_layout()

plt.show()

4. 基于直方图的阈值选择

通过观察图像的直方图,可以更准确地选择阈值。

//python

python 复制代码
import cv2

import numpy as np

from matplotlib import pyplot as plt

 #读取图像
img = cv2.imread('coins.jpg', 0)


 #计算直方图
hist = cv2.calcHist([img], [0], None, [256], [0, 256])


 #手动设定阈值
thresh_value = 130

maxval = 255

 #应用阈值
ret, thresh = cv2.threshold(img, thresh_value, maxval, cv2.THRESH_BINARY_INV)



 #显示结果
plt.figure(figsize=(15, 6))

 #原始图像和阈值分割结果
plt.subplot(131), plt.imshow(img, cmap='gray'), plt.title('Original Image')

plt.subplot(132), plt.imshow(thresh, cmap='gray'), plt.title(f'Thresholding (T={thresh_value})')


 #直方图
plt.subplot(133)

plt.plot(hist)

plt.axvline(x=thresh_value, color='r', linestyle='')

plt.title('Image Histogram')

plt.xlabel('Pixel Value')

plt.ylabel('Frequency')

plt.xlim([0, 256])


plt.tight_layout()

plt.show()

三、自适应阈值分割

当图像的光照不均匀或存在阴影时,全局阈值分割可能会导致分割效果不佳。这时可以使用自适应阈值分割,它根据图像的局部区域动态调整阈值。

在OpenCV中,cv2.adaptiveThreshold()函数用于实现自适应阈值分割。

  1. cv2.adaptiveThreshold()函数

//python

dst = cv2.adaptiveThreshold(src, maxval, adaptiveMethod, thresholdType, blockSize, C)

参数说明:

src:输入图像(必须是灰度图像)

maxval:当像素值超过阈值(或小于阈值,根据thresholdType而定)时,赋予的新值

adaptiveMethod:自适应阈值计算方法

cv2.ADAPTIVE_THRESH_MEAN_C:阈值为邻域内像素的平均值减去常数C

cv2.ADAPTIVE_THRESH_GAUSSIAN_C:阈值为邻域内像素的高斯加权平均值减去常数C

thresholdType:阈值类型,只能是cv2.THRESH_BINARY或cv2.THRESH_BINARY_INV

blockSize:邻域大小(必须是奇数,如3, 5, 7...)

C:从平均值或加权平均值中减去的常数(可以是正数或负数)

2. 自适应阈值与全局阈值的对比

//python

python 复制代码
import cv2

import numpy as np

from matplotlib import pyplot as plt

 #读取图像
img = cv2.imread('page.jpg', 0)

 #全局阈值
ret, thresh_global = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)

 #自适应阈值  均值法
thresh_mean = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C,

                                   cv2.THRESH_BINARY, 11, 2)

 #自适应阈值  高斯法
thresh_gaussian = 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, thresh_global, thresh_mean, thresh_gaussian]



plt.figure(figsize=(15, 10))

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.tight_layout()

plt.show()

3. 自适应阈值参数的影响

//python

python 复制代码
import cv2

import numpy as np

from matplotlib import pyplot as plt

 #读取图像

img = cv2.imread('page.jpg', 0)

 #不同blockSize的效果

thresh1 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,

                               cv2.THRESH_BINARY, 5, 2)

thresh2 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,

                               cv2.THRESH_BINARY, 11, 2)

thresh3 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,

                               cv2.THRESH_BINARY, 19, 2)



 #不同C值的效果

thresh4 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,

                               cv2.THRESH_BINARY, 11, 2)

thresh5 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,

                               cv2.THRESH_BINARY, 11, 2)

thresh6 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,

                               cv2.THRESH_BINARY, 11, 6)



 #显示结果

plt.figure(figsize=(15, 12))

 blockSize的影响

plt.subplot(2, 3, 1), plt.imshow(thresh1, 'gray'), plt.title('blockSize=5')

plt.subplot(2, 3, 2), plt.imshow(thresh2, 'gray'), plt.title('blockSize=11')

plt.subplot(2, 3, 3), plt.imshow(thresh3, 'gray'), plt.title('blockSize=19')

 #C值的影响

plt.subplot(2, 3, 4), plt.imshow(thresh4, 'gray'), plt.title('C=2')

plt.subplot(2, 3, 5), plt.imshow(thresh5, 'gray'), plt.title('C=2')

plt.subplot(2, 3, 6), plt.imshow(thresh6, 'gray'), plt.title('C=6')

plt.tight_layout()

plt.show()

四、Otsu's自动阈值分割

Otsu's算法是一种自动阈值选择方法,它通过最大化类间方差来确定最优阈值。在OpenCV中,可以通过在cv2.threshold()函数的type参数中添加cv2.THRESH_OTSU标志来启用Otsu's算法。

1. Otsu's算法的原理

Otsu's算法的核心思想是:假设图像的直方图是双峰的,将图像分为前景和背景两部分,通过最大化这两部分的类间方差来确定最优阈值。

类间方差公式:

sigma_b^2 = w0 w1 (mu0 mu1)^2

其中:

w0:背景像素的比例

w1:前景像素的比例(w1 = 1 w0)

mu0:背景像素的平均灰度值

mu1:前景像素的平均灰度值

2. OpenCV中的Otsu's实现

//python

python 复制代码
import cv2

import numpy as np

from matplotlib import pyplot as plt

 #读取图像

img = cv2.imread('coins.jpg', 0)

 #应用Otsu's阈值分割

ret_otsu, thresh_otsu = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)

 #应用普通全局阈值分割(使用Otsu's得到的阈值)

ret_normal, thresh_normal = cv2.threshold(img, ret_otsu, 255, cv2.THRESH_BINARY_INV)

 #显示结果

plt.figure(figsize=(15, 6))

 #原始图像和直方图

plt.subplot(131), plt.imshow(img, cmap='gray'), plt.title('Original Image')

 #阈值分割结果

plt.subplot(132), plt.imshow(thresh_normal, cmap='gray'), plt.title(f'Global Thresholding (T={ret_otsu})')

plt.subplot(133), plt.imshow(thresh_otsu, cmap='gray'), plt.title(f'Otsu\'s Thresholding (T={ret_otsu})')

plt.tight_layout()

plt.show()

 显示直方图和Otsu's阈值

plt.figure(figsize=(10, 5))

hist = cv2.calcHist([img], [0], None, [256], [0, 256])

plt.plot(hist)

plt.axvline(x=ret_otsu, color='r', linestyle='', label=f'Otsu\'s Threshold: {ret_otsu}')

plt.title('Image Histogram with Otsu\'s Threshold')

plt.xlabel('Pixel Value')

plt.ylabel('Frequency')

plt.legend()

plt.xlim([0, 256])

plt.show()

3. Otsu's算法与高斯模糊结合

当图像噪声较大时,可以先进行高斯模糊处理,再应用Otsu's阈值分割:

//python

python 复制代码
import cv2

import numpy as np

from matplotlib import pyplot as plt

 #读取图像

img = cv2.imread('noisy_image.jpg', 0)

 #直接应用Otsu's阈值分割

ret1, thresh1 = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)

 #先高斯模糊,再应用Otsu's阈值分割

blurred = cv2.GaussianBlur(img, (5, 5), 0)

ret2, thresh2 = cv2.threshold(blurred, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)

 #显示结果

plt.figure(figsize=(15, 8))

 #原始图像、直接Otsu、高斯模糊+Otsu

plt.subplot(221), plt.imshow(img, cmap='gray'), plt.title('Original Noisy Image')

plt.subplot(222), plt.imshow(thresh1, cmap='gray'), plt.title(f'Otsu\'s Thresholding (T={ret1})')

plt.subplot(223), plt.imshow(blurred, cmap='gray'), plt.title('Gaussian Blurred Image')

plt.subplot(224), plt.imshow(thresh2, cmap='gray'), plt.title(f'Blur + Otsu\'s (T={ret2})')

plt.tight_layout()

plt.show()

五、全局阈值与自适应阈值的对比

1. 对比实验

//python

python 复制代码
import cv2

import numpy as np

from matplotlib import pyplot as plt

 #读取光照不均匀的图像

img = cv2.imread('uneven_lighting.jpg', 0)

 #全局阈值分割

ret1, thresh1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)

 #Otsu's阈值分割

ret2, thresh2 = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

 #自适应阈值分割  均值法

thresh3 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C,

                               cv2.THRESH_BINARY, 11, 2)

 #自适应阈值分割  高斯法

thresh4 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,

                               cv2.THRESH_BINARY, 11, 2)

 #显示结果

plt.figure(figsize=(15, 10))

titles = ['Original Image', 'Global Thresholding (T=127)',

          "Otsu's Thresholding", 'Adaptive Mean Thresholding',

          'Adaptive Gaussian Thresholding']

images = [img, thresh1, thresh2, thresh3, thresh4]

for i in range(5):

    plt.subplot(2, 3, i+1)

    plt.imshow(images[i], 'gray')

    plt.title(titles[i])

    plt.xticks([]), plt.yticks([])

plt.tight_layout()

plt.show()

2. 优缺点对比

六、实际应用案例

1. 文档扫描

//python

python 复制代码
import cv2

import numpy as np

from matplotlib import pyplot as plt

 #读取文档图像

img = cv2.imread('document.jpg')

 #转换为灰度图像

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

 #高斯模糊,减少噪声

blurred = cv2.GaussianBlur(gray, (5, 5), 0)

 #自适应阈值分割

thresh = cv2.adaptiveThreshold(blurred, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,

                              cv2.THRESH_BINARY, 11, 2)

 #显示结果

plt.figure(figsize=(15, 8))

plt.subplot(131), plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)), plt.title('Original Document')

plt.subplot(132), plt.imshow(gray, cmap='gray'), plt.title('Grayscale')

plt.subplot(133), plt.imshow(thresh, cmap='gray'), plt.title('Adaptive Thresholding')

plt.tight_layout()

plt.show()

2. 硬币检测

//python

python 复制代码
import cv2

import numpy as np

from matplotlib import pyplot as plt

 #读取图像
img = cv2.imread('coins.jpg')

 #转换为灰度图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

 #高斯模糊
blurred = cv2.GaussianBlur(gray, (11, 11), 0)

 #Otsu's阈值分割
ret, thresh = cv2.threshold(blurred, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)

 #形态学操作,去除噪声
kernel = np.ones((3, 3), np.uint8)

opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=2)

 #检测轮廓
contours, hierarchy = cv2.findContours(opening.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

 #绘制轮廓
img_contours = img.copy()

cv2.drawContours(img_contours, contours, 1, (0, 255, 0), 2)

 #显示结果
plt.figure(figsize=(15, 8))

plt.subplot(221), plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)), plt.title('Original Image')

plt.subplot(222), plt.imshow(blurred, cmap='gray'), plt.title('Gaussian Blur')

plt.subplot(223), plt.imshow(thresh, cmap='gray'), plt.title(f'Otsu\'s Thresholding (T={ret})')

plt.subplot(224), plt.imshow(cv2.cvtColor(img_contours, cv2.COLOR_BGR2RGB)), plt.title(f'Coins Detected: {len(contours)}')

plt.tight_layout()

plt.show()

七、阈值分割的技巧与注意事项

1. 图像预处理

灰度转换:阈值分割只适用于灰度图像,需要先将彩色图像转换为灰度图像

噪声处理:使用高斯模糊(cv2.GaussianBlur())减少图像噪声,提高阈值分割效果

对比度增强:使用直方图均衡化(cv2.equalizeHist())增强图像对比度,使阈值分割更容易

2. 阈值类型的选择

目标为白色,背景为黑色:使用cv2.THRESH_BINARY

目标为黑色,背景为白色:使用cv2.THRESH_BINARY_INV

保留图像的亮度信息:使用cv2.THRESH_TRUNC或cv2.THRESH_TOZERO

3. 自适应阈值参数调整

blockSize:邻域大小,通常为奇数(3, 5, 7...)。值越大,阈值对局部变化越不敏感

C:从平均值或加权平均值中减去的常数。值越大,二值图像越暗;值越小,二值图像越亮

4. 多阈值分割

对于复杂图像,可以使用多阈值分割,将图像分为多个区域:

//python

python 复制代码
import cv2

import numpy as np

from matplotlib import pyplot as plt

 #读取图像

img = cv2.imread('multi_threshold.jpg', 0)

 #多阈值分割

ret1, thresh1 = cv2.threshold(img, 50, 255, cv2.THRESH_BINARY)

ret2, thresh2 = cv2.threshold(img, 120, 255, cv2.THRESH_BINARY)

ret3, thresh3 = cv2.threshold(img, 180, 255, cv2.THRESH_BINARY)

 #组合多阈值结果

result = np.zeros_like(img)

result[img <= 50] = 0       #黑色

result[(img > 50) & (img <= 120)] = 85    #暗灰色

result[(img > 120) & (img <= 180)] = 170   #灰色

result[img > 180] = 255      #白色

 #显示结果

plt.figure(figsize=(15, 10))

titles = ['Original Image', 'Thresholding (T=50)',

          'Thresholding (T=120)', 'Thresholding (T=180)',

          'Multithreshold Result']

images = [img, thresh1, thresh2, thresh3, result]

for i in range(5):

    plt.subplot(2, 3, i+1)

    plt.imshow(images[i], 'gray')

    plt.title(titles[i])

    plt.xticks([]), plt.yticks([])

plt.tight_layout()

plt.show()

八、总结

阈值分割是图像处理中的基础技术,OpenCV提供了丰富的函数来实现各种阈值分割方法。

主要内容回顾

  1. 全局阈值分割:

使用同一个阈值处理整个图像,适用于光照均匀的图像

cv2.threshold()函数

多种阈值类型:二值化、反向二值化、截断、化零等
2. 自适应阈值分割:

根据局部区域动态调整阈值,适用于光照不均匀的图像

cv2.adaptiveThreshold()函数

两种自适应方法:均值法和高斯法
3. Otsu's自动阈值:

自动计算最优阈值,适用于直方图呈双峰分布的图像

在cv2.threshold()中添加cv2.THRESH_OTSU标志启用

可与高斯模糊结合使用,提高抗噪声能力
选择建议

如果图像光照均匀,对比度高,选择全局阈值分割

如果图像直方图呈双峰分布,选择Otsu's阈值分割

如果图像光照不均匀或存在阴影,选择自适应阈值分割

如果图像噪声较大,先进行高斯模糊处理,再应用阈值分割

通过合理选择阈值分割方法和调整参数,可以在不同的应用场景中获得最佳的分割效果。阈值分割是许多高级图像处理任务的基础,如轮廓检测、目标识别等。

相关推荐
我是人机不吃鸭梨3 小时前
Flutter AI 集成革命(2025版):从 Gemini 模型到智能表单验证器的终极方案
开发语言·javascript·人工智能·flutter·microsoft·架构
AI即插即用3 小时前
即插即用系列 | CVPR 2025 FDConv:频域动态卷积,打破密集预测任务的参数效率瓶颈
图像处理·人工智能·深度学习·神经网络·计算机视觉·cnn·视觉检测
呆萌很4 小时前
NVIDIA CUDA Toolkit 安装
人工智能
天庭鸡腿哥4 小时前
Vivo出品,干趴付费!
人工智能·语音识别
code 旭4 小时前
神经网络+激活函数+损失函数 三合一速查表
人工智能·深度学习·神经网络
laplace01234 小时前
讲清楚 Prompt, Agent, MCP 是什么+大模型测评流程
人工智能·语言模型·prompt·transformer
CoovallyAIHub4 小时前
摄像头如何“看懂”你的手势?手势识别实现新人机交互
深度学习·算法·计算机视觉
Gofarlic_oms14 小时前
区块链存证节点搭建:金融行业审计证据链构建指南
运维·人工智能·金融·数据挖掘·区块链·需求分析·devops
AI科技星4 小时前
张祥前统一场论:空间位移条数概念深度解析
数据结构·人工智能·经验分享·算法·计算机视觉