OpenCV Python:光照不均文档照片的最优阈值处理方案咨询
兄弟,我太懂你这个困扰了!之前做文档扫描工具的时候,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

