文章目录
1. 阈值处理
在图像处理领域,阈值处理(Thresholding)是一种简单而有效的技术,用于将灰度图像转化为二值图像(即每个像素点要么是黑色,要么是白色)。这种处理方式常用于分割图像中的前景和背景,或者提取特定的形状或物体。
2. 阈值处理的基本原理
阈值处理的基本思想是设定一个阈值 T T T,然后遍历图像中的每一个像素。如果像素的灰度值高于阈值,就将其设为白色(通常为255);如果低于阈值,则将其设为黑色(通常为0)。公式如下:
g
(
x
,
y
)
=
{
255
,
i
f
f
(
x
,
y
)
>
T
0
,
i
f
f
(
x
,
y
)
≤
T
(1)
g(x,y)= \begin{cases} 255,if\quad f(x,y)>T\\ 0, if\quad f(x,y)\leq T \end{cases} \tag{1}
g(x,y)={255,iff(x,y)>T0,iff(x,y)≤T(1)
其中,
f
(
x
,
y
)
f(x,y)
f(x,y) 是原始图像的灰度值,
g
(
x
,
y
)
g(x,y)
g(x,y) 是处理后的二值图像。
3. 常见的阈值处理方法
3.1 全局阈值(Global Thresholding):
- 在这种方法中,整个图像使用同一个阈值。适合于图像对比度较高且背景简单的情况。OpenCV 中常用的函数是
cv2.threshold
。
import cv2
# 读取灰度图像
image = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)
# 全局阈值处理
_, thresh_global = cv2.threshold(image, 127, 255, cv2.THRESH_BINARY)
# 显示结果
cv2.imshow('Global Thresholding', thresh_global)
cv2.waitKey(0)
cv2.destroyAllWindows()
3.2 自适应阈值(Adaptive Thresholding):
自适应阈值方法会根据图像的局部区域动态计算阈值,因此对于光照不均或对比度较低的图像更有效。自适应阈值根据局部窗口内的像素值计算每个像素的阈值,从而避免了全局阈值对整个图像的统一处理。
3.2.1 工作原理
自适应阈值(Adaptive Thresholding)的工作原理是根据图像局部区域的特征来动态调整阈值,从而处理光照不均或对比度不够的图像。它的主要思想是对于图像中的每一个像素,根据其周围邻域的像素值来确定一个局部阈值。这样可以避免全局阈值方法可能带来的效果不佳的问题,尤其是在图像的不同区域光照差异较大的情况下。
- 计算临近区域的加权平均值:
对于图像中的每一个像素点,自适应阈值方法会考虑该像素点周围的一定范围内的像素值。这些像素值形成了一个局部区域或窗口。
通过计算这个局部区域内的像素值的加权平均(或均值),得到一个阈值。加权平均意味着某些像素对计算结果的影响比其他像素更大,通常是窗口中心的像素权重更高。
- 获得阈值:
计算出的加权平均值(或均值)被用作该像素点的局部阈值。这个阈值代表了该像素所在局部区域的“亮度”水平。
- 对当前像素点进行处理:
使用计算得到的局部阈值来决定该像素点的类别。具体地说:
如果当前像素的值大于或等于局部阈值,则将其分类为前景(通常设为 255,白色)。
如果当前像素的值小于局部阈值,则将其分类为背景(通常设为 0,黑色)。
3.2.2 工作步骤
1. 局部邻域计算:
- 将图像划分为多个小的局部区域(通常称为窗口或块),在每个局部区域内计算一个局部阈值。这个局部阈值基于该区域内的像素值来动态确定。
2. 局部阈值计算:
对于每个局部区域,计算其局部阈值的常用方法包括均值和高斯加权平均:
-
均值自适应阈值(Adaptive Mean Thresholding): 阈值等于邻域像素的平均值减去一个常量。公式如下:
-
公式: T ( x , y ) = m e a n ( I ( x , y ) ) − C T(x,y)=mean(I(x,y))−C T(x,y)=mean(I(x,y))−C
-
其中:
- T ( x , y ) T(x,y) T(x,y)是位置 ( x , y ) (x,y) (x,y) 的阈值。
- m e a n ( I ( x , y ) ) mean(I(x,y)) mean(I(x,y)) 是位置 ( x , y ) (x,y) (x,y) 的邻域内像素的平均值。
- C C C 是一个常数,用于调整阈值。
-
在这种方法中,阈值是由邻域内像素的均值减去一个常数 C C C计算得到的。常数 C C C用于避免噪声对分割结果的影响。
-
高斯自适应阈值(Adaptive Gaussian Thresholding): 阈值等于邻域像素的加权平均值(权重为高斯窗口)减去一个常量。公式如下:
- T ( x , y ) = m e a n ( I ( x , y ) ) − C ⋅ s t d ( I ( x , y ) ) T(x,y)=mean(I(x,y))−C⋅std(I(x,y)) T(x,y)=mean(I(x,y))−C⋅std(I(x,y))
- 其中:
- T ( x , y ) T(x,y) T(x,y) 是位置 ( x , y ) (x,y) (x,y) 的阈值。
- m e a n ( I ( x , y ) ) mean(I(x,y)) mean(I(x,y)) 是位置 ( x , y ) (x,y) (x,y)的邻域内像素的平均值。
- s t d ( I ( x , y ) ) std(I(x,y)) std(I(x,y)) 是位置 ( x , y ) (x,y) (x,y)的邻域内像素的标准差。
- C C C是一个常数,用于调整阈值。
在这种方法中,阈值是由邻域内像素的均值减去标准差的 C C C倍计算得到的。这样可以更好地处理局部的光照变化和噪声。
3.二值化处理:
- 对于每个像素点,将其灰度值与计算得到的局部阈值进行比较。如果灰度值大于局部阈值,则将其设置为白色;否则,设置为黑色。
3.2.3 适用场景
- 图像光照不均匀或有明显的阴影。
- 需要在复杂背景下进行分割。
3.2.4 优缺点
自适应阈值的优点
- 适应光照变化: 能够处理光照不均匀的图像,避免全局阈值方法对不同区域处理不一致的问题。
- 局部处理: 通过局部窗口计算阈值,可以更好地处理细节和复杂背景。
自适应阈值的缺点
- 计算复杂度较高: 由于需要对每个像素计算局部阈值,处理时间相对较长。
- 参数选择: 自适应阈值方法中的参数(如窗口大小和常量)需要根据具体图像调整,以获得最佳效果。
import cv2
# 读取灰度图像
image = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)
# 自适应均值阈值处理
thresh_mean = cv2.adaptiveThreshold(image, 255, cv2.ADAPTIVE_THRESH_MEAN_C,
cv2.THRESH_BINARY, 11, 2)
# 自适应高斯阈值处理
thresh_gaussian = cv2.adaptiveThreshold(image, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY, 11, 2)
# 显示结果
cv2.imshow('Adaptive Mean Thresholding', thresh_mean)
cv2.imshow('Adaptive Gaussian Thresholding', thresh_gaussian)
cv2.waitKey(0)
cv2.destroyAllWindows()
自定义实现(不使用 OpenCV)
如果你需要从头开始实现自适应阈值处理,以下是一个简化的 Python 代码示例:
import numpy as np
import cv2
def adaptive_threshold(image, block_size, C):
rows, cols = image.shape
padded_image = np.pad(image, pad_width=block_size//2, mode='constant', constant_values=0)
result = np.zeros_like(image)
for i in range(rows):
for j in range(cols):
block = padded_image[i:i+block_size, j:j+block_size]
mean = np.mean(block)
threshold = mean - C
result[i, j] = 255 if image[i, j] >= threshold else 0
return result
# 读取图像(以灰度模式)
image = cv2.imread('input_image.png', cv2.IMREAD_GRAYSCALE)
# 设置自适应阈值参数
block_size = 11
constant = 2
# 计算自适应阈值
adaptive_thresh = adaptive_threshold(image, block_size, constant)
# 显示结果
cv2.imshow('Original Image', image)
cv2.imshow('Adaptive Threshold', adaptive_thresh)
# 等待按键并销毁窗口
cv2.waitKey(0)
cv2.destroyAllWindows()
# 保存处理后的图像(可选)
cv2.imwrite('adaptive_thresh_custom.png', adaptive_thresh)
3.3 Otsu’s 方法:
Otsu’s 阈值法是一种自动选择阈值的全局方法。它通过分析图像的灰度直方图,找到能够最大化类间方差(前景和背景)的阈值。Otsu’s 方法适用于双峰分布的图像(即前景和背景的灰度值分布相对独立且存在两个峰值)。
什么是最大化类间方差
最大化类间方差(Between-Class Variance)是 Otsu’s 阈值法的核心概念,旨在选择一个最佳阈值,使得图像的前景和背景之间的差异最大化。这种方法通过分析图像的灰度直方图,找到一个阈值,使得图像被分割成前景和背景后,它们之间的类间方差最大。
工作原理
Otsu’s 方法遍历所有可能的阈值,并计算每个阈值对应的类间方差,选择使类间方差最大的阈值作为最终的阈值。
适用场景
- 图像的灰度直方图呈现双峰分布。
- 需要自动选择最优阈值。
优缺点
- 优点: 自动选择阈值,适用于双峰图像的分割。
- 缺点: 对多峰分布的图像效果不佳,计算量相对较大。
公式推导链接:https://blog.csdn.net/leonardohaig/article/details/120269341
OpenCV 中可以通过设置 cv2.THRESH_OTSU
来使用。
import cv2
# 读取灰度图像
image = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)
# Otsu's 阈值处理
_, thresh_otsu = cv2.threshold(image, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
# 显示结果
cv2.imshow('Otsu Thresholding', thresh_otsu)
cv2.waitKey(0)
cv2.destroyAllWindows()
代码
import cv2
import numpy as np
# 读取图像并转换为灰度图像
image = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)
# 全局阈值处理
_, thresh_global = cv2.threshold(image, 127, 255, cv2.THRESH_BINARY)
# 自适应阈值处理
thresh_adaptive = cv2.adaptiveThreshold(image, 255, cv2.ADAPTIVE_THRESH_MEAN_C,
cv2.THRESH_BINARY, 11, 2)
# Otsu's 阈值处理
_, thresh_otsu = cv2.threshold(image, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
# 显示结果
cv2.imshow('Global Thresholding', thresh_global)
cv2.imshow('Adaptive Thresholding', thresh_adaptive)
cv2.imshow('Otsu Thresholding', thresh_otsu)
cv2.waitKey(0)
cv2.destroyAllWindows()
4. 应用场景
- 文档图像的二值化:用于将扫描的文档图像转换为黑白图像,以便进一步的 OCR(光学字符识别)处理。
- 物体检测:在自动化检测系统中,阈值处理可以用于分割目标物体,以便进行形状分析或特征提取。
- 医学图像处理:用于分割细胞、器官或其他生物结构。
阈值处理是图像预处理中常用的步骤,尤其适合对噪声较少、对比度较高的图像进行处理。根据具体应用的需要,可以选择合适的阈值处理方法。
总结
- 全局阈值适合简单、光照均匀的图像,计算速度快但对复杂场景效果不好。
- 自适应阈值适合光照不均的复杂图像,能更好地处理细节,但计算复杂度较高。
- Otsu’s 阈值法是一种自动选择阈值的全局方法,适合灰度直方图呈双峰分布的图像。