Opencv(四):自适应二值化
文章目录
- 前言
- 一、为什么需要二值化?
- 二、自适应二值化的基本思想
- 三、主要参数详解
- 四、两种常见算法原理详解
- 1. 基于均值的自适应阈值法
- 2. 基于高斯加权的自适应阈值法
- 举个例子:
- 一维高斯函数的推导:
 
- 3. 核(Kernel)与 σ 的关系
 
- 五、Python + OpenCV 实现自适应二值化
- 1. 灰度化处理
- 2. 自适应均值法
- 3. 自适应高斯法
 
- 六、实验对比与结果分析
- 七、参数调优经验总结
- 八、完整代码示例
- 十一、总结与心得
 
前言
在图像处理的世界里,二值化是一道绕不过去的“基础关”。无论是 OCR 文字识别、车牌检测、医学影像,还是目标分割,几乎所有视觉任务在进入算法核心前,都要经历“灰度化 + 二值化”这一步。
但如果你用过最常见的全局阈值方法(如 cv2.threshold()),你就会发现一个痛点:
 光照不均、阴影、反光区域都会导致效果极差。
 这时候,自适应二值化(Adaptive Thresholding) 就能派上大用场。
本文将带你从理论、公式、代码到实验结果,完整搞懂自适应二值化的原理与实践,力求让你读完就能真正掌握它的底层逻辑与实战技巧。
一、为什么需要二值化?
图像是由像素矩阵构成的,每个像素有一个灰度值(0~255)。在很多任务中,我们只关心前景(目标)和背景,而不是具体的灰度细节。这就需要“二值化”,即:
把灰度图中每个像素根据阈值 T 转换为 0 或 255。
公式表示为:

在 OpenCV 中,我们可以通过以下代码轻松实现:
import cv2img = cv2.imread('text.png', 0)
ret, binary = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
cv2.imshow('binary', binary)
cv2.waitKey(0)
但是,这种全局阈值方式有一个明显缺陷——假设整张图只用一个固定阈值。
 这意味着如果图片左边亮、右边暗,同样的阈值在两边的效果会完全不同。
如下图示意(左亮右暗),使用单一阈值时,亮区容易过曝、暗区丢失细节:
亮区(>127) → 全白     暗区(<127) → 全黑
结果就是:目标边缘模糊、文字断裂、细节丢失。
 这就是为什么在光照不均或对比度变化大的场景中,全局二值化行不通。
二、自适应二值化的基本思想
自适应二值化(Adaptive Thresholding)的核心思想是:
“不要为整张图设置一个阈值,而是为每个像素动态计算属于它自己的阈值。”
换句话说,它把整幅图像分成多个小区域(称为“局部窗口”或“核 block”),对每个区域独立计算阈值,再进行二值化。这使得算法能灵活应对局部亮度变化。
其基本公式如下:
 
其中:
- f():表示邻域内的统计函数(如均值或高斯加权平均)
- C:常数修正项,用于微调阈值
- 邻域(x, y):指以 (x, y) 为中心、大小为 blockSize×blockSize 的小区域
OpenCV 提供的函数为:
cv2.adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C)
三、主要参数详解
| 参数名 | 含义 | 常见取值 | 
|---|---|---|
| src | 输入图像(必须为灰度图) | cv2.imread('xxx', 0) | 
| maxValue | 二值化后的最大值 | 通常为255 | 
| adaptiveMethod | 阈值计算方法 | ADAPTIVE_THRESH_MEAN_C或ADAPTIVE_THRESH_GAUSSIAN_C | 
| thresholdType | 二值化类型 | THRESH_BINARY或THRESH_BINARY_INV | 
| blockSize | 邻域块大小(奇数) | 3, 5, 7, 9, … | 
| C | 常数,结果 = f(邻域) - C | 通常取 2~10 | 
四、两种常见算法原理详解
OpenCV 支持两种自适应方法:
 1.基于均值的自适应阈值法(ADAPTIVE_THRESH_MEAN_C)
 2.基于高斯加权的自适应阈值法(ADAPTIVE_THRESH_GAUSSIAN_C)
接下来我们详细拆解这两种算法。
1. 基于均值的自适应阈值法
这种方法最容易理解:
 它取以当前像素为中心、blockSize×blockSize 区域的像素均值作为阈值。
数学表达式为:

其中:
- N = blockSize²
- C 为常数偏移量,用于调节整体亮度。
实现逻辑:
- 以当前像素为中心取一个邻域;
- 计算邻域内灰度值的平均;
- 平均值减去 C;
- 用得到的阈值对当前像素进行二值化。
示例:
假设左上角像素周围的 3×3 邻域灰度值为:
162 163 165
160 159 161
158 157 160
邻域平均值为:
mean = (162+163+165+160+159+161+158+157+160)/9 = 160.56
若 C = 5,则阈值 T = 155.56。
 若该像素值 > 155.56 → 输出 255,否则输出 0。
这种方法实现简单、计算快,但受噪声影响较大,因为每个像素的权重相同。
2. 基于高斯加权的自适应阈值法
高斯加权法在计算邻域均值时,引入了加权概念——距离中心越近的像素,权重越高。
公式为:

 其中,G(i,j)表示二维高斯权重核:
 
高斯分布的特点是中心值最大,离中心越远权重越低,这样可以让局部区域的中心像素对阈值影响更大,更适合光照渐变的场景。
举个例子:
对于一个 3×3 的高斯核,权重矩阵大致如下:
0.0625  0.125  0.0625
0.125   0.25   0.125
0.0625  0.125  0.0625
它的权重和为 1,这样加权平均不会改变亮度总体水平。
一维高斯函数的推导:

通过不同的 σ(标准差)可以调节分布的“宽窄”。σ 越大,曲线越平;σ 越小,曲线越尖。
在二维扩展下(图像),一般取 σx = σy = σ。
3. 核(Kernel)与 σ 的关系
在 OpenCV 中,当 blockSize ≤ 7 且 σ <= 0(未指定)时,系统会自动使用固定核值:
| kernel | 权重分布(近似值) | 
|---|---|
| 1 | [1] | 
| 3 | [0.25, 0.5, 0.25] | 
| 5 | [0.0625, 0.25, 0.375, 0.25, 0.0625] | 
| 7 | [0.03125, 0.109375, 0.21875, 0.28125, 0.21875, 0.109375, 0.03125] | 
五、Python + OpenCV 实现自适应二值化
下面我们来动手实现并观察效果。
1. 灰度化处理
import cv2img = cv2.imread('demo.png')       # 读取彩色图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv2.imshow('Gray', gray)
cv2.waitKey(0)
灰度化是二值化的前置步骤,因为自适应算法基于灰度计算阈值。
2. 自适应均值法
binary_mean = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 2
)
cv2.imshow('Adaptive Mean', binary_mean)
cv2.waitKey(0)
这里:
- blockSize = 11 → 每个像素用 11×11 邻域计算;
- C = 2 → 计算出的阈值减去 2;
- THRESH_BINARY表示高于阈值为白,否则为黑。
3. 自适应高斯法
binary_gauss = cv2.adaptiveThreshold(gray, 255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY,11, 2
)
cv2.imshow('Adaptive Gaussian', binary_gauss)
cv2.waitKey(0)
可以直观对比两种算法的区别:
- 均值法:阈值分布较平滑,但在阴影区易受噪声影响。
- 高斯法:边缘更柔和,效果更稳定。
六、实验对比与结果分析
为了更清晰地看到两种算法的差异,我们使用同一张文字图片进行实验:
| 方法 | 参数设置 | 效果说明 | 
|---|---|---|
| 全局阈值法 | T=127 | 光照不均处明显失真,左亮右暗区域文字模糊 | 
| 自适应均值法 | blockSize=11, C=2 | 能适应部分光照变化,但噪声略多 | 
| 自适应高斯法 | blockSize=11, C=2 | 阈值变化平滑,整体效果最佳 | 
结论:
 在明暗分布不均的场景下,高斯加权法的效果显著优于均值法。
 对噪声敏感的图像可以先做 cv2.GaussianBlur() 预处理。
七、参数调优经验总结
-  blockSize 的选择 - 必须是奇数(3、5、7、9、11…)
- 值越大,局部范围越广,结果越平滑
- 值太小,会对噪声过敏
- 一般经验:图像宽度的 1/30 ~ 1/50 较合适
 
-  C 值的调整 - 增大 C → 阈值整体降低 → 图像变暗(白色区域减少)
- 减小 C → 阈值整体升高 → 图像变亮
- 通常 2~10 之间调整即可
 
-  adaptiveMethod 的选择 - MEAN_C速度快,适合简单场景
- GAUSSIAN_C效果好,适合复杂光照
 
-  预处理建议 - 若图像噪声多:先 cv2.medianBlur()去噪
- 若亮度变化大:可以尝试 cv2.equalizeHist()进行直方图均衡化再二值化
 
- 若图像噪声多:先 
八、完整代码示例
import cv2
#对图像做自适应二值化处理#1.读取一张想要自适应二值化的彩色图
image_np = cv2.imread('./lena.png')
image_np = cv2.resize(image_np,(400,400))
# 2.对彩色图像进行灰度化
image_gray = cv2.cvtColor(image_np,cv2.COLOR_BGR2GRAY)#3.对转化好的灰度图进行自适应二值化
#cv2.adaptiveThreshold:用来对单通道图进行自适应二值化的
# 第一个参数:单通道图
# 第二个参数:二值化过程中所用到的最大值
# 第三个参数:自适应二值化的方法:1.平均值法 cv2.ADAPTIVE_THRESH_MEAN 2.使用高斯核的加权平均法cv2.ADAPTIVE_THRESH_MEAN_C
#第四个参数:二值化的方法:1.阈值法:THRESH_BINARY  2.反阈值法:THRESH_BINARY_INV
#第五个参数:blocksize:核的大小,通常为奇数 3*3,5*5
#第六个参数:要减去的常数C的大小:通常为正数,但也有可能是0或者负数
image_binary = cv2.adaptiveThreshold(image_gray,255,cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY,5,10)#4.对计算结果进行显示
cv2.imshow('image_np',image_np)
cv2.imshow('image_gray',image_gray)
cv2.imshow('image_binary',image_binary)cv2.waitKey(0)
运行后可以明显看到三张图的差异:
- 原图中光照不均;
- 均值法效果中边缘略粗;
- 高斯法结果边缘细腻、文字清晰。
 输出结果:
  
  
  
十一、总结与心得
自适应二值化通过为每个像素动态计算阈值,解决了光照不均、阴影干扰等问题,使得二值化结果更稳健、更具普适性。
掌握它的关键要点有三:
- 理解局部阈值思想(每个像素独立计算);
- 熟悉两种方法的区别(均值法 vs 高斯法);
- 灵活调节 blockSize 与 C,获得最佳平衡。
