You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

OpenCV Python:光照不均文档照片的最优阈值处理方案咨询

解决OpenCV文档扫描光照不均的实战改进方案

兄弟,我太懂你这个困扰了!之前做文档扫描工具的时候,Otsu阈值在光照均匀的场景下确实省心,但一碰到那种半边亮半边暗、或者有阴影的文档,直接就崩了。给你几个我亲测有效的改进方向,都是实战里用下来靠谱的:

1. 用自适应阈值替代全局Otsu

全局Otsu是拿整张图的灰度分布来计算阈值,局部光照不均的时候,这个全局阈值肯定适配不了所有区域。自适应阈值就聪明多了——它会把图像切成一个个小区域,每个区域单独计算阈值,完美适配局部的明暗差异。

代码示例:

import cv2

# 转灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 高斯加权自适应阈值:blockSize必须是奇数,C是微调阈值的常数
thresh = cv2.adaptiveThreshold(
    gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
    cv2.THRESH_BINARY_INV, 11, 2
)

你可以根据文档里文字的大小调整blockSize,比如文字越大,这个值可以设得越大(比如15、21这类奇数)。

2. 先做CLAHE对比度校正再阈值

普通的直方图均衡化会把亮部拉得更亮、暗部压得更暗,反而容易丢失文字细节。而**CLAHE(限制对比度自适应均衡)**会限制每个小区域的对比度提升幅度,既能提亮暗部文字,又不会让亮部过曝,对光照不均的文档特别友好。

代码示例:

import cv2

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 创建CLAHE对象:clipLimit控制对比度上限,tileGridSize是分块大小
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
# 应用CLAHE到灰度图
clahe_gray = clahe.apply(gray)
# 再对校正后的图用Otsu阈值
_, thresh = cv2.threshold(clahe_gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)

如果你的文档阴影特别重,可以适当调高clipLimit(比如到3.0)试试。

3. 背景建模法抵消光照影响

这个思路更直接:先把文档的纯背景(没有文字的区域)提取出来,然后用原图除以背景,就能抵消掉光照的不均匀性——因为背景的灰度变化就是光照的变化,相除后就能得到统一亮度的文字区域。

代码示例:

import cv2

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 用形态学开运算提取背景:kernel要比最大的文字块大,才能完全去掉文字
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (50, 50))
background = cv2.morphologyEx(gray, cv2.MORPH_OPEN, kernel)
# 原图除以背景,再归一化到0-255范围
corrected = cv2.divide(gray, background, scale=255)
# 最后做阈值处理
_, thresh = cv2.threshold(corrected, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)

注意kernel的尺寸要根据你的文档调整,如果文字很大,就把这个值调大一些。

4. 彩色文档用Lab颜色空间单独处理亮度

如果是彩色文档,直接转灰度处理可能会损失一些颜色相关的信息(比如彩色文字)。这时候可以转成Lab颜色空间,单独对亮度通道(L通道)做校正,再转回BGR,既解决光照问题,又保留颜色。

代码示例:

import cv2

# 转Lab颜色空间
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
l_channel, a_channel, b_channel = cv2.split(lab)

# 对L通道做CLAHE校正
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
l_clahe = clahe.apply(l_channel)

# 合并通道转回BGR
lab_corrected = cv2.merge((l_clahe, a_channel, b_channel))
img_corrected = cv2.cvtColor(lab_corrected, cv2.COLOR_LAB2BGR)

# 再转灰度做阈值处理
gray = cv2.cvtColor(img_corrected, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)

这些方法还可以组合使用,比如先做CLAHE校正,再用自适应阈值,效果会更稳定。你可以根据自己的文档场景选最适合的,或者多试试组合~

内容的提问来源于stack exchange,提问作者Martin Eckleben

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 11:07:56