Python+OpenCV图像处理(五)——图像阈值和二值化


系列文章
Python+OpenCV图像处理(一)——OpenCV框架与图像插值算法
Python+OpenCV图像处理(二)——几何变换
Python+OpenCV图像处理(三)——彩色空间互换
Python+OpenCV图像处理(四)——图像滤波
Python+OpenCV图像处理(五)——图像阈值和二值化
Python+OpenCV图像处理(六)——边缘检测

目录

    • 五、图像阈值和二值化
      • 5.1 简介
      • 5.2 简单阈值
      • 5.3 最大类间方差法(大津阈值法)
      • 5.4 自适应阈值


五、图像阈值和二值化

5.1 简介

图像阈值化分割是进行图像分析、特征提取与模式识别之前的必要的图像预处理过程。图像阈值化的目的是要按照灰度级,对像素集合进行一个划分,得到的每个子集形成一个与现实景物相对应的区域,各个区域内部具有一致的属性,而相邻区域不具有这种一致属性。

5.2 简单阈值

在这里,问题直截了当。对于每个像素,应用相同的阈值。如果像素值小于阈值,则将其设置为0,否则将其设置为最大值。函数cv.threshold用于应用阈值。第一个参数是源图像,它应该是灰度图像。第二个参数是阈值,用于对像素值进行分类。第三个参数是分配给超过阈值的像素值的最大值。OpenCV提供了不同类型的阈值,这由函数的第四个参数给出。通过使用cv.THRESH_BINARY类型。所有简单的阈值类型为

对于图像阈值化分割,最简单的方法是,如果像素值高于阈值(TH),将其设为最大值,如果像素值低于阈值,则将其设为最小值。举个例子,对于像素值为0~255的图像,大于TH,设为255。小于TH,设为0。这种简单的方法也被称作二值化。在OpenCV中可以使用cv2.THRESH_BINARY实现。常用的简单阈值分割的原理图如下:

Python+OpenCV图像处理(五)——图像阈值和二值化_第1张图片
  • 二值阈值化 —— 像素值大于阈值的设为最大值,小于阈值的设为最小值
  • 反向二值阈值化 —— 像素值大于阈值的设为最小值,小于阈值的设为最大值
  • 截断阈值化 —— 像素值大于阈值的设为阈值,小于阈值的保持原来的像素值
  • 超过阈值被置0 —— 像素值大于阈值的置为0,小于阈值的保持原来的像素值
  • 低于阈值被置0 —— 像素值大于阈值的保持原来的像素值,小于阈值的置为0

这几种简单阈值的方法对应OpenCV中的函数为:

  • cv2.THRESH_BINARY
  • cv2.THRESH_BINARY_INV
  • cv2.THRESH_TRUNC
  • cv2.THRESH_TOZERO
  • cv2.THRESH_TOZERO_INV
Python+OpenCV图像处理(五)——图像阈值和二值化_第2张图片

这些函数都有两个返回值,第一个返回值为使用的阈值,第二个就是阀值化之后的图像了。

实现

#%%

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

img = cv2.imread('lena.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.savefig("bigpic_2.png")
plt.show()

cv2.imshow('Original Image', img)
cv2.imshow('BINARY', thresh1)
cv2.imshow('BINARY_INV', thresh2)
cv2.imshow('TRUNC', thresh3)
cv2.imshow('TOZERO', thresh4)
cv2.imshow('TOZERO_INV', thresh5)

cv2.waitKey(0)
cv2.destroyAllWindows()

Python+OpenCV图像处理(五)——图像阈值和二值化_第3张图片

tips:这里不能用plt进行输出,plt输出如下图,会发现有些许偏差,偏差如上图和下图的对比,具体原因暂时未找出,找出后及时更新

Python+OpenCV图像处理(五)——图像阈值和二值化_第4张图片

5.3 最大类间方差法(大津阈值法)

对于图像二值化的简单阈值法,我们需要自己提供一个阈值,而大津法(OTSU)可以根据图像特性,选择最佳的阈值,故它也被认为是图像分割中阈值选取的最佳算法,计算简单,不受图像亮度和对比度的影响。从大津法的原理上来讲,该方法又称作最大类间方差法,因为按照大津法求得的阈值进行图像二值化分割后,前景与背景图像的类间方差最大。

它是按图像的灰度特性,将图像分成背景和前景两部分。因方差是灰度分布均匀性的一种度量,背景和前景之间的类间方差越大,说明构成图像的两部分的差别越大,当部分前景错分为背景或部分背景错分为前景都会导致两部分差别变小。因此,使类间方差最大的分割意味着错分概率最小。

应用: 是求图像全局阈值的最佳方法,应用不言而喻,适用于大部分需要求图像全局阈值的场合。

优点: 计算简单快速,不受图像亮度和对比度的影响。

缺点: 对图像噪声敏感;只能针对单一目标分割;当目标和背景大小比例悬殊、类间方差函数可能呈现双峰或者多峰,这个时候效果不好。

原理非常简单,涉及的知识点就是均值、方差等概念和一些公式推导。为了便于理解,我们从目的入手,反推一下这著名的OTSU算法。

求类间方差:

OTSU算法的假设是存在阈值TH将图像所有像素分为两类C1(小于TH)和C2(大于TH),则这两类像素各自的均值就为m1、m2,图像全局均值为mG。同时像素被分为C1和C2类的概率分别为p1、p2。因此就有:

Python+OpenCV图像处理(五)——图像阈值和二值化_第5张图片

根据原文,式(4)还可以进一步变形:

Python+OpenCV图像处理(五)——图像阈值和二值化_第6张图片
#%%

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

img = cv2.imread('lena.jpg',0)
# 全局阈值
ret1,th1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY)
# Otsu阈值
ret2,th2 = cv2.threshold(img,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)

cv2.imshow('Original Image', img)
cv2.imshow('BINARY', th1)
cv2.imshow('OTSU', th2)

cv2.waitKey(0)
cv2.destroyAllWindows()

Python+OpenCV图像处理(五)——图像阈值和二值化_第7张图片

5.4 自适应阈值

不管是前面的简单阈值法,还是后面的OTSU算法,但这些算法属于全局阈值法,所以对于某些光照不均的图像,这种全局阈值分割的方法会显得苍白无力,如下图:

根据图像上的每一个小区域计算与其对应的阀值。因此在同一幅图像上的不同区域采用的是不同的阀值,从而使我们能在亮度不同的情况下得到更好的结果。
这种方法需要我们指定三个参数,返回值只有一个。
Adaptive Method 指定计算阀值的方法
-cv2.ADAPTIVE_THRESH_MEAN_C:阀值取自相邻区域的平均值
-cv2.ADAPTIVE_THRESH_GAUSSIAN_C:阀值取自相邻区域的加权和,权重为一个高斯窗口
Block Size 邻域大小(用来计算阀值的区域大小)
C这就是一个常数,阀值就等于的平均值或者加权平均值减去这个常数

#%%

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

img = cv2.imread('18.png',0)

ret, th1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY)

# Otsu阈值
ret2,th2 = cv2.threshold(img,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)

# 11为block size,2为C值
th3 = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_MEAN_C , cv2.THRESH_BINARY,11,2 )
th4 = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C , cv2.THRESH_BINARY,11,2)

titles = ['original image', 'global thresholding (v=127)', 'otus thresholding','Adaptive mean thresholding', 'adaptive gaussian thresholding']
images = [img,th1,th2,th3,th4]

for i in range(5):
    plt.subplot(2,3,i+1),plt.imshow(images[i],'gray')
    plt.title(titles[i])
    plt.xticks([]),plt.yticks([])
#plt.savefig('duibi.png')
plt.show()

cv2.imshow('Original', img)
cv2.imshow('BINARY', th1)
cv2.imshow('OTSU', th2)
cv2.imshow('ADAPTIVE_THRESH_MEAN', th3)
cv2.imshow('ADAPTIVE_THRESH_GAUSSIAN', th4)

cv2.waitKey(0)
cv2.destroyAllWindows()

Python+OpenCV图像处理(五)——图像阈值和二值化_第8张图片
Python+OpenCV图像处理(五)——图像阈值和二值化_第9张图片

你可能感兴趣的:(cv)