OpenCV内阈值处理方法

阈值分割是指剔除图像内高于一定值或者低于一定值的像素点。例如,设定阈值为“127”,将图像内:

l所有像素值大于127的像素点设为255

l所有像素值小于等于127的像素点设为0

通过上述方式,会得到一幅二值图像。如图6- 1所示,按照上述阈值分割方式将一副灰度图像处理为一幅二值图像,有效地实现了前景和背景的分离。

 

图6- 阈值分割示例

在OpenCV中提供了函数cv2.threshold()和函数cv2.adaptiveThreshold()实现阈值分割。

6.1 threshold函数

在OpenCV3.0内使用cv2.threshold()函数进行阈值化处理,该函数的语法格式为:

retval, dst = cv2.threshold( src, thresh, maxval, type )

式中:

lretval – 返回的阈值。

ldst – 阈值分割结果图像,与原始图像具有相同的大小和类型。

lsrc – 要进行阈值分割的图像,可以是多通道的,832的浮点型

lthresh – 要设定的阈值。

lmaxval – 当type参数为THRESH_BINARY或者THRESH_BINARY_INV类型时,需要设定的最大值。

ltype – 阈值分割的类型,具体类型值如表6- 1所示。

表6- 阈值分割类型

 

 

上述公式相对抽象,可以将其可视化,具体如图6- 2所示。

图6- 阈值类型

6.1.1 二值化阈值(cv2.THRESH_BINARY

二值化阈值是指将原始图像处理为仅有两个值的二值图像,其示意图如图6- 4所示。其针对像素点的处理方式为:

l灰度值大于阈值的像素点,将其灰度值设定为最大值。

l灰度值小于等于阈值的像素点,将其灰度值设定为0。

图6- 二值阈值化

如果使用表达式表示,其目标值的产生规则为:

 

 

式中,是选定的特定阈值。

在8位图像中,最大值是255。因此,在对8位灰度图像进行二值化时,如果将阈值设定为127,那么:

l所有大于127的像素点会被处理为255

l其余值会被处理为0。

为了说明上的方便,后续说明中,我们都以8位图为例说明,即其最大值为255。

【例6.1】使用函数cv2.threshold()对数组进行二值化阈值处理,观察处理结果。

根据题目要求,编写代码如下:

import cv2
import numpy as np
img=np.random.randint(0,256,size=[4,5],dtype=np.uint8)
t,rst=cv2.threshold(img,127,255,cv2.THRESH_BINARY)
print("img=\n",img)
print("t=",t)
print("rst=\n",rst)

 

运行程序,结果如下所示:

img=

 [[184 204  23 247 118]

 [173 107 120  69 209]

 [231 218  42 211 108]

 [133 125  29 191 198]]

t= 127.0

rst=

 [[255 255   0 255   0]

 [255   0   0   0 255]

 [255 255   0 255   0]

 [255   0   0 255 255]]

【例6.2使用函数cv2.threshold()对图像进行二值化阈值处理。

根据题目要求,编写代码如下:

import cv2
img=cv2.imread("lena.bmp")
t,rst=cv2.threshold(img,127,255,cv2.THRESH_BINARY)
cv2.imshow("img",img)
cv2.imshow("rst",rst)
cv2.waitKey()
cv2.destroyAllWindows()

 

运行程序,结果如图6- 4所示。图中左侧是原始图像,右侧是二值化阈值处理结果。

 

图6- 二值化阈值处理结果

6.1.2 反二值化阈值(cv2.THRESH_BINARY_INV

反二值化阈值的处理结果仍旧是仅有两个值的二值图像,与二值化阈值不同之处在于对像素值的处理方式不同,反二值化阈值针对像素点的处理方式为:

l大于阈值的像素点,将其值设定为0

l小于等于阈值的像素点,将其值设定为255

反二值化阈值处理方式的示意图如图6- 5所示。

图6- 二值阈值化

如果使用表达式表示,其目标值的产生规则为:

 

 

 

式中,thresh是选定的阈值。

【例6.3使用函数cv2.threshold()对数组进行反二值化阈值处理,观察处理结果。

根据题目要求,编写代码如下:

 

import cv2
import numpy as np
img=np.random.randint(0,256,size=[4,5],dtype=np.uint8)
t,rst=cv2.threshold(img,127,255,cv2.THRESH_BINARY_INV)
print("img=\n",img)
print("t=",t)
print("rst=\n",rst)

 

运行程序,结果如下所示:

img=

 [[ 56  64 150  48  41]

 [108 165 112 213 110]

 [122 244  10 213  46]

 [247  30  90   0  26]]

t= 127.0

rst=

 [[255 255   0 255 255]

 [255   0 255   0 255]

 [255   0 255   0 255]

 [  0 255 255 255 255]]

【例6.4使用函数cv2.threshold()对图像进行反二值化阈值处理。

根据题目要求,编写代码如下:

 

import cv2
img=cv2.imread("lena.bmp")
t,rst=cv2.threshold(img,127,255,cv2.THRESH_BINARY_INV)
cv2.imshow("img",img)
cv2.imshow("rst",rst)
cv2.waitKey()
cv2.destroyAllWindows()

 

运行程序,结果如图6- 6所示。图中左侧是原始图像,右侧是反二值化阈值处理结果。

 

图6- 反二值化阈值结果

6.1.3 截断阈值化(cv2.THRESH_TRUNC

截断阈值化是指将图像中大于阈值的像素点设定为阈值,小于该阈值的像素点保持不变。其处理方式的示意图如图6- 7所示。

图6- 截断阈值化

例如:阈值选取为127,则截断阈值化处理时:

l大于127的像素点,其值被设定为127。

l小于等于127像素点,其值保持改变。

如果使用表达式表示,其目标值的产生规则为:

 

 

 

【例6.5使用函数cv2.threshold()对数组进行截断阈值化处理,观察处理结果。

根据题目要求,编写代码如下:

 

import cv2
import numpy as np
img=np.random.randint(0,256,size=[4,5],dtype=np.uint8)
t,rst=cv2.threshold(img,127,255,cv2.THRESH_TRUNC)
print("img=\n",img)
print("t=",t)
print("rst=\n",rst)

 

运行程序,结果如下所示:

img=

 [[190  60 146  22  22]

 [253  49  63 180 113]

 [ 27  64 148  44   7]

 [ 11 100 249  47 239]]

t= 127.0

rst=

 [[127  60 127  22  22]

 [127  49  63 127 113]

 [ 27  64 127  44   7]

 [ 11 100 127  47 127]]

【例6.6使用函数cv2.threshold()对图像进行截断阈值化处理。

根据题目要求,编写代码如下:

 

import cv2
img=cv2.imread("lena.bmp")
t,rst=cv2.threshold(img,127,255,cv2.THRESH_TRUNC)
cv2.imshow("img",img)
cv2.imshow("rst",rst)
cv2.waitKey()
cv2.destroyAllWindows()

 

运行程序,结果如图6- 8所示。图中左侧是原始图像,右侧是截断阈值化处理结果。

 

图6- 截断阈值化处理结果

6.1.4 超阈值零处理(cv2.THRESH_TOZERO_INV

超阈值零处理是指将图像中大于阈值的像素点处理为0,小于该阈值的保持不变。即先选定一个阈值,然后对图像做如下处理:

l大于阈值的像素点,像素值处理为0

l小于等于阈值的像素点,值保持不变。

超阈值零处理的工作原理如图6- 9所示。

图6- 超阈值零处理

例如:阈值选取为127,则:

l大于127的像素点设定为0

l小于等于127的像素点保持改变

如果使用表达式表示,其目标值的产生规则为:

 

 

 

【例6.7使用函数cv2.threshold()对数组进行超阈值零处理,观察处理结果。

根据题目要求,编写代码如下:

 

import cv2
import numpy as np
img=np.random.randint(0,256,size=[4,5],dtype=np.uint8)
t,rst=cv2.threshold(img,127,255,cv2.THRESH_TOZERO_INV)
print("img=\n",img)
print("t=",t)
print("rst=\n",rst)

 

运行程序,结果如下所示:

img=

 [[ 16  68 231  94  82]

 [112 195 239 127  71]

 [ 67  47 240 107 227]

 [144  51 130 207 164]]

t= 127.0

rst=

 [[ 16  68   0  94  82]

 [112   0   0 127  71]

 [ 67  47   0 107   0]

 [  0  51   0   0   0]]

【例6.8使用函数cv2.threshold()对图像进行超阈值零处理。

根据题目要求,编写代码如下:

 

import cv2
img=cv2.imread("lena.bmp")
t,rst=cv2.threshold(img,127,255,cv2.THRESH_TOZERO_INV)
cv2.imshow("img",img)
cv2.imshow("rst",rst)
cv2.waitKey()
cv2.destroyAllWindows()

 

运行程序,结果如图6- 10所示。图中左侧是原始图像,右侧是超阈值零处理结果。

 

图6- 10 超阈值零处理结果

6.1.5 低阈值零处理(cv2.THRESH_TOZERO

低阈值零处理是指将图像中小于等于阈值的像素点处理为0大于阈值的像素点保持不变。即先选定一个阈值,然后对图像做如下处理:

l大于阈值的像素点,值保持不变;

l小于等于阈值的像素点,值变为0

其示意图如图6- 11所示。

图6- 11 低阈值零处理

例如:阈值选取为127,则:

l大于127的像素点,像素值保持改变

l小于等于127的像素点,像素值设定为0

如果使用表达式表示,其目标值的产生规则为:

 

 

 

【例6.9使用函数cv2.threshold()对数组进行低阈值零处理,观察处理结果。

根据题目要求,编写代码如下:

 

import cv2
import numpy as np
img=np.random.randint(0,256,size=[4,5],dtype=np.uint8)
t,rst=cv2.threshold(img,127,255,cv2.THRESH_TOZERO)
print("img=\n",img)
print("t=",t)
print("rst=\n",rst)

 

运行程序,结果如下所示:

img=

 [[ 73  57 135  10   8]

 [127 162 110 217  54]

 [203  99 205  97 127]

 [ 14 100  83 226  71]]

t= 127.0

rst=

 [[  0   0 135   0   0]

 [ 0 162   0 217   0]

 [203   0 205   0   0]

 [  0   0   0 226   0]]

【例6.10使用函数cv2.threshold()对图像进行低阈值零处理。

根据题目要求,编写代码如下:

 

import cv2
img=cv2.imread("lena.bmp")
t,rst=cv2.threshold(img,127,255,cv2.THRESH_TOZERO)
cv2.imshow("img",img)
cv2.imshow("rst",rst)
cv2.waitKey()
cv2.destroyAllWindows()

 

运行程序,结果如图6- 12所示。图中左侧是原始图像,右侧是低阈值零处理结果。

 

图6- 12 低阈值零处理结果

6.2 自适应阈值

对于色彩均衡的图像,直接使用一个阈值就能完成对图像的阈值化处理。但是,有时图像色彩是不均衡的,此时如果仅仅使用一个阈值处理,就无法得到清晰有效的阈值分割结果图像。

有一种改进的阈值技术,其使用变化的阈值完成对图像的阈值处理。这种技术被称为自适应阈值处理。自适应阈值在进行阈值处理时,通过计算每个像素周围临近区域的加权平均值获得阈值,并使用该阈值对当前像素点进行处理。相比较普通的阈值处理方法,自适应阈值处理能够更好地处理明暗差异较大的图像。

OpenCV提供了函数cv2.adaptiveThreshold()实现自适应阈值分割,该函数的语法格式为:

dst = cv.adaptiveThreshold( src, maxValue, adaptiveMethod, thresholdType, blockSize, C )

式中:

ldst:自适应阈值处理结果;

lsrc:要进行处理的源图像。需要注意的是,该图像必须是8位单通道的图像;

lmaxValue:最大值;

ladaptiveMethod:自适应方法;

lthresholdType:阈值处理方式,该值必须是cv2.THRESH_BINARY或者cv2.THRESH_BINARY_INV二者之一;

lblockSize:块大小。表示一个像素在计算其阈值时所使用的邻域尺寸,通常为3/5/7等;

lC:常量。

函数cv2.adaptiveThreshold()根据参数adaptiveMethod来确定自适应阈值的方法。函数包含cv2.ADAPTIVE_THRESH_MEAN_Ccv2.ADAPTIVE_THRESH_GAUSSIAN_C两种不同的方法。这两种方法都是逐个像素地计算自适应阈值,自适应阈值通过计算每个像素由“参数blockSize”所指定邻域的加权平均值减去“常量C”得到。两种不同的方法,计算邻域的加权平均值时所使用的方式有所不同:

lcv2.ADAPTIVE_THRESH_MEAN_C:,邻域所有像素点的权值是一致的;

lcv2.ADAPTIVE_THRESH_GAUSSIAN_C:根据邻域各个像素点到中心点的距离通过高斯方程得到权重值;

【例6.11对一幅图像分别使用二值化阈值函数cv2.threshold()和自适应阈值函数cv2.adaptiveThreshold()进行处理,观察处理结果的差异。

根据题目要求,编写代码如下:

 

import cv2
img=cv2.imread("computer.jpg",0)
t1,thd=cv2.threshold(img,127,255,cv2.THRESH_BINARY)
athdMEAN=cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY,5,3)
athdGAUS=cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY,5,3)
cv2.imshow("img",img)
cv2.imshow("thd",thd)
cv2.imshow("athdMEAN",athdMEAN)
cv2.imshow("athdGAUS",athdGAUS)
cv2.waitKey()
cv2.destroyAllWindows()

 

运行程序,结果如图6- 13所示。图中:

l图(a)是原始图像

l图(b)是二值化阈值处理结果

l图(c)是自适应阈值采用参数cv2.ADAPTIVE_THRESH_MEAN_C的处理结果

l图(d)是自适应阈值采用参数cv2.ADAPTIVE_THRESH_GAUSSIAN_C的处理结果

   

       (a)       (b)       (c)       (d)

图6- 13 处理结果

通过对比普通的阈值分割与自适应阈值分割可以发现,自适应阈值分割保留了更多的细节信息。在一些极端情况下,普通的阈值分割会丢失大量的信息,而自适应可以得到效果更好的二值图像。

6.3 OTSU处理

在使用函数cv2.threshold()进行阈值分割时,需要自定义一个阈值,并以此阈值作为图像阈值分割的依据。通常情况下,处理的是色彩均衡的图像,这时直接将阈值设为“127”是比较合适的。

但是,有时图像灰度级的分布是不均衡的,如果此时还将阈值设置为“127”,那么阈值分割的结果就是失败的。例如,有一个图像img,里面的值为:

[[123 123 123 123 123]

 [123 123 123 123 123]

 [123 123 126 126 126]

 [123 123 126 126 126]

 [123 123 126 126 126]]

此时,如果仍旧以127作为阈值,那么分割结果是:

[[0 0 0 0 0]

 [0 0 0 0 0]

 [0 0 0 0 0]

 [0 0 0 0 0]

 [0 0 0 0 0]]

很显然,这不是我们想要的结果。对于img,我们可以观察到如果以阈值“125”进行分割,可以得到较好的结果:

[[  0   0   0   0   0]

 [  0   0   0   0   0]

 [  0   0 255 255 255]

 [  0   0 255 255 255]

 [  0   0 255 255 255]]

但是,在实际处理中图像是复杂的,不太可能像上述img一样,一眼就观察出最合适的阈值。如果一个个去尝试,工作量无疑是巨大的。

Otsu方法能够根据当前图像给出最佳的类间分割阈值。简而言之,Otsu方法就是遍历所有可能阈值,从而找到最佳的阈值。

在OpenCV中,提供了实现Otsu方法的方式。通过在函数cv2.threshold()中对参数type的类型多传递一个参数“cv2.THRESH_OTSU”即可实现Otsu方式的阈值分割。

需要说明的是,在使用Otsu方法时,要把阈值设为0此时的函数cv2.threshold()会自动寻找最优阈值,并将该阈值返回。例如,如下语句:

t,otsu=cv2.threshold(img,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)

该语句中,让函数cv2.threshold()采用Otsu的方式进行阈值分割。与普通的阈值分割不同之处在于:

l参数type,增加了一个参数值“cv2.THRESH_OTSU

l设定的阈值为0

l返回值t是Otsu方法计算得到并使用的最优阈值

需要注意,如果采用普通的阈值分割,返回的阈值就是设定的阈值。例如:

t,thd=cv2.threshold(img,127,255,cv2.THRESH_BINARY)

设定了阈值为“127”,所以最终的返回值“t=127”。

【例6.12测试Otsu阈值处理的实现。

根据题目要求,编写代码如下:

 

import cv2
import numpy as np
img = np.zeros((5,5),dtype=np.uint8)
img[0:6,0:6]=123
img[2:6,2:6]=126
print("img=\n",img)
t1,thd=cv2.threshold(img,127,255,cv2.THRESH_BINARY)
print("thd=\n",thd)
t2,otsu=cv2.threshold(img,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
print("otsu=\n",otsu)

 

运行程序,结果如下所示。

img=

 [[123 123 123 123 123]

 [123 123 123 123 123]

 [123 123 126 126 126]

 [123 123 126 126 126]

 [123 123 126 126 126]]

thd=

 [[0 0 0 0 0]

 [0 0 0 0 0]

 [0 0 0 0 0]

 [0 0 0 0 0]

 [0 0 0 0 0]]

otsu=

 [[  0   0   0   0   0]

 [  0   0   0   0   0]

 [  0   0 255 255 255]

 [  0   0 255 255 255]

 [  0   0 255 255 255]]

【例6.13对一幅图像分别使用普通的二值化阈值处理和Otsu阈值处理,观察处理结果的差异。

根据题目要求,编写代码如下:

 

import cv2
img=cv2.imread("tiffany.bmp",0)
t1,thd=cv2.threshold(img,127,255,cv2.THRESH_BINARY)
t2,otsu=cv2.threshold(img,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
cv2.imshow("img",img)
cv2.imshow("thd",thd)
cv2.imshow("otus",otsu)
cv2.waitKey()
cv2.destroyAllWindows()

 

运行程序,结果如图6- 14所示。图中:

l图(a)是原始图像;

l图(b)是普通二值化阈值采用阈值为127点的处理结果;

l图(c)是二值化阈值采用参数cv2.THRESH_OTSU后的处理结果。

  

图6- 14 处理结果

本例中,图像整体的亮度较高,即有较多的像素值都是大于127的。所以:

l在使用127作为阈值进行普通的二值阈值化时,得到了大量的白色;

l在使用Otsu处理时,因为通过计算采用了最优阈值,所以得到了较好的处理结果。


本内容节选自李立宗主编的《OpenCV轻松入门——面向Python》,电子工业出版社。

参加视频学习:

https://edu.csdn.net/course/detail/10552

 

微信搜索【cvlight】,关注【计算机视觉之光】,学习计算机视觉知识。

 

 

 

 

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

superdont

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值