如何用OpenCV和Python实现不受颜色差异影响的图像阈值化?
解决明暗反转图像的统一阈值化问题
嘿,我来帮你搞定这个阈值化的难题!这种一张图字母亮于背景、另一张字母暗于背景的情况,确实会让普通固定阈值化失效,咱们用自适应判断+动态阈值化的思路就能统一输出白背景黑字母的结果。
核心思路
关键在于先判断每张图里「字母是亮的还是暗的」,然后动态选择阈值化的方向(直接二值化/反转二值化),具体步骤如下:
1. 先将图像转为灰度图
不管输入是彩色还是灰度图,先统一转成灰度通道,简化后续处理:
import cv2 img = cv2.imread("your_image_path.jpg") gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
2. 用Otsu法自动计算最优阈值
Otsu阈值法会自动分析图像的像素分布,找到能最大化前景(字母)和背景差异的阈值,比手动设阈值靠谱得多:
thresh_val, _ = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
3. 判断字母的明暗属性
因为字母的面积通常远小于背景,我们可以通过统计「阈值以下的暗像素数量」和「阈值以上的亮像素数量」来判断:
- 如果暗像素更少 → 字母是深色的(背景亮),直接用普通二值化
- 如果亮像素更少 → 字母是浅色的(背景暗),用反转二值化来翻转明暗
代码实现:
# 统计暗像素和亮像素的数量 dark_pixel_count = cv2.countNonZero(gray < thresh_val) total_pixels = gray.shape[0] * gray.shape[1] light_pixel_count = total_pixels - dark_pixel_count # 动态选择阈值化方式 if dark_pixel_count < light_pixel_count: # 字母为深色,直接二值化:背景白,字母黑 _, final_thresh = cv2.threshold(gray, thresh_val, 255, cv2.THRESH_BINARY) else: # 字母为浅色,反转二值化:翻转后背景白,字母黑 _, final_thresh = cv2.threshold(gray, thresh_val, 255, cv2.THRESH_BINARY_INV)
完整可运行代码
把上面的步骤封装成函数,方便批量处理两张图:
import cv2 def get_uniform_black_text_white_bg(img_path): # 读取图像 img = cv2.imread(img_path) if img is None: raise ValueError(f"无法读取图像:{img_path}") # 转灰度 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # Otsu自动计算阈值 thresh_val, _ = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) # 统计明暗像素数量 dark_count = cv2.countNonZero(gray < thresh_val) total_pixels = gray.size light_count = total_pixels - dark_count # 生成统一结果 if dark_count < light_count: _, result = cv2.threshold(gray, thresh_val, 255, cv2.THRESH_BINARY) else: _, result = cv2.threshold(gray, thresh_val, 255, cv2.THRESH_BINARY_INV) return result # 测试两张图 result1 = get_uniform_black_text_white_bg("light_text_dark_bg.jpg") result2 = get_uniform_black_text_white_bg("dark_text_light_bg.jpg") # 保存结果 cv2.imwrite("uniform_result1.jpg", result1) cv2.imwrite("uniform_result2.jpg", result2)
特殊情况处理
如果你的字母和背景面积差不多(比如全填充的大字母),可以换一种判断逻辑:
- 取图像四个角落的像素(假设角落都是背景),计算背景的平均亮度
- 随机取图像中心区域的几个像素(假设是字母),对比中心和角落的亮度
- 如果中心比角落暗 → 字母是深色;如果中心比角落亮 → 字母是浅色,再决定是否反转
这种方法更适合极端场景,不过大部分普通图文场景用前面的像素数量统计法就足够了。
内容的提问来源于stack exchange,提问作者BuddyRJ
相关产品推荐
相关产品推荐

