欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

OpenCV 从零学习 · 第 6 讲:阈值化(threshold / adaptiveThreshold,把图变黑白、自动找阈值)

OpenCV 从零学习 · 第 6 讲:阈值化

函数:cv2.threshold / cv2.adaptiveThreshold | 难度:★☆☆☆☆

阈值化(Thresholding)就是把图像按"亮度"切成两类:黑 / 白(0 / 255)。它是后续轮廓查找、边缘检测、形态学这些操作的前置步骤——很多算法只吃二值图(黑白图)。本讲讲清两种阈值:全局阈值自适应阈值,以及怎么让程序"自动"找出阈值。


一、全局阈值 cv2.threshold

函数签名

retval, dst = cv2.threshold(src, thresh, maxval, type)
参数 含义
src输入图,必须是单通道灰度图
thresh阈值(如 127)。用 OTSU/TRIANGLE 时此值被忽略,传 0
maxval满足条件时赋予的像素值,通常填 255
type阈值类型(见下表)

返回值retval 是实际使用的阈值(全局固定时等于 thresh,OTSU 时为算法算出的);dst 是二值结果图。

type 的五种取值

type 规则 直观效果
THRESH_BINARYsrc > thresh → maxval,否则 0亮处白、暗处黑
THRESH_BINARY_INVsrc > thresh → 0,否则 maxval与上者反相
THRESH_TRUNCsrc > thresh → thresh,否则保持原值亮部被"削平",整体保留
THRESH_TOZEROsrc > thresh → 原值,否则 0只保留亮区,暗部变黑
THRESH_TOZERO_INVsrc > thresh → 0,否则原值只保留暗区,亮部变黑
场景 A · 扫描文档转纯黑白
把白纸黑字的扫描件二值化:cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)。文字是暗的(<127)变 0(黑),纸面是亮的(>127)变 255(白)。
场景 B · 提取暗色物体
若目标是暗色(如黑墨水滴),用 THRESH_BINARY_INV 让暗处变白,方便后续轮廓查找把它当"前景"。
场景 C · TRUNC 保留层次
不想丢失灰度层次、只想压掉最亮的部分(如过曝天空),用 THRESH_TRUNC,超过阈值的像素被压到阈值,其余不变——结果仍是灰度图,不是纯黑白。
import cv2

gray = cv2.imread('doc.jpg', cv2.IMREAD_GRAYSCALE)
_, bin_img   = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)        # 黑白
_, trunc_img = cv2.threshold(gray, 200, 255, cv2.THRESH_TRUNC)        # 削平过曝
_, zero_img  = cv2.threshold(gray, 100, 255, cv2.THRESH_TOZERO)       # 只留亮区

二、自动阈值:OTSU 与 TRIANGLE

固定阈值 127 在光照不均时效果差。OpenCV 能在 type 里"或"上 cv2.THRESH_OTSU(大津法)或 cv2.THRESH_TRIANGLE(三角法),让程序自动算出最佳阈值——此时 thresh 参数传 0 被忽略,真正阈值由 retval 返回。

场景 D · 光照不均的文档
左边用固定 127 把阴影区文字也压黑了;换成 OTSU 后程序自动找到分割线,阴影里的字也能保留。
_, otsu = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
print('OTSU 算出的阈值 =', _)   # 例如 118

三、自适应阈值 cv2.adaptiveThreshold

当图像局部光照差异很大(如一张纸一半在阴影里),全局阈值(哪怕 OTSU)仍会失败。自适应阈值对每个像素,看它周围一小块区域的均值来定自己的门槛。

函数签名

dst = cv2.adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C)
参数 含义
maxValue满足条件时赋的值,通常 255
adaptiveMethodADAPTIVE_THRESH_MEAN_C(邻域均值)或 ADAPTIVE_THRESH_GAUSSIAN_C(高斯加权)
thresholdType一般只用 THRESH_BINARY
blockSize邻域大小,必须是奇数(如 11、15)
C从均值里减去的常数,微调阈值(常取 2~10)
场景 E · 阴影文档 / 手机拍的纸
同一张图:固定阈值把阴影区文字弄丢;ADAPTIVE_THRESH_GAUSSIAN_C 逐块判定,阴影里和亮处的字都清晰保留。
adapt = cv2.adaptiveThreshold(
    gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
    cv2.THRESH_BINARY, blockSize=15, C=8)
⚠ 新手必踩坑
  • threshold 输入必须是单通道灰度图;彩色图直接传进去结果异常——记得先 cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
  • 用 OTSU / TRIANGLE 时,thresh 必须传 0(写 127 会被忽略,但语义上是"由算法决定",传 0 最安全)。
  • adaptiveThresholdblockSize 必须是奇数,写偶数会报错。

本讲要点

  • 全局阈值 cv2.threshold(src, thresh, maxval, type):固定一条线切黑白,五种 type 决定"切法"。
  • 光照不均用 THRESH_OTSU 自动找阈值;局部明暗差异极大用 adaptiveThreshold
  • 三种方法输入都要灰度图;自适应阈值的 blockSize 取奇数。

下一讲:第 7 讲 · 边缘检测(Sobel / Laplacian / Canny)——把二值/灰度图变成"轮廓线",是轮廓查找的前一步。

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功