OpenCV-Python——第11章:图像阈值

本文详细介绍了图像处理中的阈值技术,包括简单阈值、自适应阈值和Otsu's二值化方法。通过实例展示了不同阈值类型的效果,如二进制、反二进制、截断、阈值化0和反阈值化0,以及如何利用自适应阈值和Otsu's二值化处理复杂图像。

目录

简单阈值    自适应阈值    Otsu's二值化


1 简单阈值

ret, dst = threshold(src, thresh, maxval, type)

  • ret: retVal(返回值),在Otsu‘s中会用到
  • dst: 目标图像
  • src: 原图像,只能输入单通道图像,通常来说为灰度图
  • thresh: 阈值
  • maxval: 当像素值超过了阈值(或者小于阈值,根据type来决定),所赋予的值
  • type:阈值类型,包含以下5种类型:

若原图直方图如下:

二进制阈值化:cv2.THRESH_BINARY   ,大于阈值取maxval,小于取0

反二进制阈值化:cv2.THRESH_BINARY_INV   ,大于阈值取0,小于取maxval

截断阈值化:cv2.THRESH_TRUNC   ,大于阈值全取阈值大小,小于则不变

阈值化0:cv2.THRESH_TOZERO   ,大于阈值取原值,小于取0

反阈值化0:cv2.THRESH_TOZERO_INV   ,大于阈值取0,小于取原值

例如:

import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('test12.jpg', 0)
ret, thresh1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
ret, thresh2 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV)
ret, thresh3 = cv2.threshold(img, 127, 255, cv2.THRESH_TRUNC)
ret, thresh4 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO)
ret, thresh5 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO_INV)
titles = ['Original Image', 'BINARY', 'BINARY_INV', 'TRUNC', 'TOZERO', 'TOZERO_INV']
images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]
for i in range(6):
    plt.subplot(2, 3, i+1), plt.imshow(images[i], 'gray')
    plt.title(titles[i])
    plt.xticks([]), plt.yticks([])
plt.show()

选用不同的原图,效果如下:

从外到内:

从左到右:

 2 自适应阈值

cv2.adaptiveThreshold(src, maxVal, adaptiveMethold, thresholdType, blockSize, C, dst)

  • src: 原图像,只能输入单通道图像,通常来说为灰度图
  • maxval: 当像素值超过了阈值(或者小于阈值,根据type来决定),所赋予的值
  • adaptiveMethold: 阈值的计算方法,包含以下2种类型:

cv2.ADAPTIVE_THRESH_MEAN_C,   阈值取自相邻区域的平均值

cv2.ADAPTIVE_THRESH_GAUSSIAN_C,   阈值取值相邻区域的加权和,权重为一个高斯窗口

  • thresholdType:二值化操作的类型,与固定阈值函数相同,包含5种类型,同上一节。
  • blockSize: 图片中分块的大小
  • C :阈值计算方法中的常数项
  • dst:目标图像

例如:

import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('test13.jpg', 0)
img = cv2.medianBlur(img, 5)  # 中值滤波

ret, th1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
# 11 为 Block size, 2 为 C 值
th2 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 2)
th3 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2)
titles = ['Original Image',
          'Global Thresholding (v = 127)', 'Adaptive Mean Thresholding', 'Adaptive Gaussian Thresholding']
images = [img, th1, th2, th3]
for i in range(4):
    plt.subplot(2, 2, i+1), plt.imshow(images[i], 'gray')
    plt.title(titles[i])
    plt.xticks([]), plt.yticks([])
plt.show()

结果如下:

3 Otsu's二值化

Otsu’s Binarization是一种基于直方图的二值化方法,它需要和threshold函数配合使用。

在第一部分中我们提到过 retVal,当我们使用 Otsu 二值化时会用到它。 那么它到底是什么呢? 在使用全局阈值时,我们就是随便给了一个数来做阈值,那我们怎么知道

我们选取的这个数的好坏呢?答案就是不停的尝试。如果是一副双峰图像(简 单来说双峰图像是指图像直方图中存在两个峰)呢?我们岂不是应该在两个峰之间的峰谷选一个值作为阈值?这就是 Otsu 二值化要做的。简单来说就是对 一副双峰图像自动根据其直方图计算出一个阈值。(对于非双峰图像,这种方法 得到的结果可能会不理想)。 这里用到到的函数还是cv2.threshold(),但是需要多传入一个参数flag:cv2.THRESH_OTSU。这时要把阈值设为 0。然后算法会找到最优阈值,这个最优阈值就是返回值 retVal。如果不使用 Otsu 二值化,返回的 retVal 值与设定的阈值相等。

Otsu过程: 
1. 计算图像直方图; 
2. 设定一阈值,把直方图强度大于阈值的像素分成一组,把小于阈值的像素分成另外一组; 
3. 分别计算两组内的偏移数,并把偏移数相加; 
4. 把0~255依照顺序多为阈值,重复1-3的步骤,直到得到最小偏移数,其所对应的值即为结果阈值。

例如:下面的例子中,输入图像是一副带有噪声的图像。第一种方法,我们设
127 为全局阈值。第二种方法,我们直接使用 Otsu 二值化。第三种方法,我 们首先使用一个9x9 的高斯核除去噪音,然后再使用 Otsu 二值化。看看噪音去除对结果的影响有多大吧。

import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('test14.jpg', 0)
# global thresholding
ret1, th1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
# Otsu's thresholding
ret2, th2 = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
# Otsu's thresholding after Gaussian filtering
# (9,9)为高斯核的大小,8 为标准差
blur = cv2.GaussianBlur(img, (9, 9), 8)
# 阈值一定要设为 0!
ret3, th3 = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
# plot all the images and their histograms
images = [img, 0, th1, img, 0, th2, blur, 0, th3]
titles = ['Original Noisy Image', 'Histogram', 'Global Thresholding (v=127)', 'Original Noisy Image',
          'Histogram', "Otsu's Thresholding", 'Gaussian filtered Image', 'Histogram', "Otsu's Thresholding"]
# 这里使用了 pyplot 中画直方图的方法,plt.hist, 要注意的是它的参数是一维数组
# 所以这里使用了(numpy)ravel 方法,将多维数组转换成一维,也可以使用 flatten 方法
# ndarray.flat 1-D iterator over an array.
# ndarray.flatten 1-D array copy of the elements of an array in row-major order
for i in range(3):
    plt.subplot(3, 3, i*3+1), plt.imshow(images[i*3], 'gray')
    plt.title(titles[i*3]), plt.xticks([]), plt.yticks([])
    plt.subplot(3, 3, i*3+2), plt.hist(images[i*3].ravel(), 256)
    plt.title(titles[i*3+1]), plt.xticks([]), plt.yticks([])
    plt.subplot(3, 3, i*3+3), plt.imshow(images[i*3+2], 'gray')
    plt.title(titles[i*3+2]), plt.xticks([]), plt.yticks([])
plt.show()

结果如下:

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值