OpenCV-Python官方文档中文翻译15:Image Thresholding图像阈值

Image Thresholding

Goal

  • In this tutorial, you will learn simple thresholding, adaptive thresholding and Otsu’s thresholding.
  • You will learn the functions cv.threshold and cv.adaptiveThreshold.
  • 在这个教程,你将学习简单阈值,自适应阈值,大津阈值。
  • 你将学习函数 cv.threshold,cv.adaptiveThreshold

Simple Thresholding

Here, the matter is straight-forward. For every pixel, the same threshold value is applied. If the pixel value is smaller than the threshold, it is set to 0, otherwise it is set to a maximum value. The function cv.threshold is used to apply the thresholding. The first argument is the source image, which should be a grayscale image. The second argument is the threshold value which is used to classify the pixel values. The third argument is the maximum value which is assigned to pixel values exceeding the threshold. OpenCV provides different types of thresholding which is given by the fourth parameter of the function. Basic thresholding as described above is done by using the type cv.THRESH_BINARY. All simple thresholding types are:

See the documentation of the types for the differences.

The method returns two outputs. The first is the threshold that was used and the second output is the thresholded image.

This code compares the different simple thresholding types:

这里,问题直接了当。对每一个像素,应用相同的阈值。如果像素值比阈值小,设为0,否则设为最大值。函数 cv.threshold被用来应用阈值。第一个参数是源图片,应该是灰度图。第二个参数是用来给像素值分类的阈值。第三个参数是分配给超过阈值的像素值的最大值。OpenCV通过函数的第四个参数来提供不同类型的阈值。上述描述的基础阈值是通过使用cv.THRESH_BINARY类型来得到的。所有的简单阈值类型有:

  • cv.THRESH_BINARY
  • cv.THRESH_BINARY_INV
  • cv.THRESH_TRUNC
  • cv.THRESH_TOZERO
  • cv.THRESH_TOZERO_INV

看类型的文档来知道类型的差别。

这个方法返回两个输出值。第一个是使用的阈值,第二个是阈值图像。

此代码比较了不同的简单阈值类型:

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img = cv.imread("gradient.png",0)
ret,thresh1 = cv.threshold(img,127,255,cv.THRESH_BINARY)
ret,thresh2 = cv.threshold(img,127,255,cv.THRESH_BINARY_INV)
ret,thresh3 = cv.threshold(img,127,255,cv.THRESH_TRUNC)
ret,thresh4 = cv.threshold(img,127,255,cv.THRESH_TOZERO)
ret,thresh5 = cv.threshold(img,127,255,cv.THRESH_TOZERO_INV)
titles = ["Original Image","BINARY","BINARY_INV","TRUNC","TOZERO","TOZERO_INV"]
images = [img,thresh1,thresh2,thresh3,thresh4,thresh5]
for i in xrange(6):
	plt.subplot(2,3,i+1),plt.imshow(images[i],"gray",vmin= 0,vmax=255)
	plt.title(titles[i])
	plt.xticks([]),plt.yticks([])
plt.show()
  • Note

    To plot multiple images, we have used the plt.subplot() function. Please checkout the matplotlib docs for more details.

The code yields this result:

  • note

    为了绘制多个图像,我们用plt.subplot()函数。请查阅matplotlib文档来获得更多细节。

代码产生了以下结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cz7Fxs7O-1614070529865)(https://docs.opencv.org/4.5.1/threshold.jpg)]

Adaptive Thresholding自适应阈值

In the previous section, we used one global value as a threshold. But this might not be good in all cases, e.g. if an image has different lighting conditions in different areas. In that case, adaptive thresholding can help. Here, the algorithm determines the threshold for a pixel based on a small region around it. So we get different thresholds for different regions of the same image which gives better results for images with varying illumination.

In addition to the parameters described above, the method cv.adaptiveThreshold takes three input parameters:

The adaptiveMethod decides how the threshold value is calculated:

The blockSize determines the size of the neighbourhood area and C is a constant that is subtracted from the mean or weighted sum of the neighbourhood pixels.

The code below compares global thresholding and adaptive thresholding for an image with varying illumination:

在上一节,我们用一个全局变量作为阈值。但是这在一些例子中可能不好,比如,如果一副图片在不同的区域有不同的光照条件。在那个例子中,自适应的阈值就很有帮助。这里,算法决定了一个像素的阈值是基于它周围的小区域。所以我们在同一张图片的不同区域用不同的阈值,这对于光照变化的图片会有更好的结果。

除了上述描述的参数外,方法 cv.adaptiveThreshold引入了三个输入参数:

adaptiveMethod方法决定了阈值是如何计算的:、

  • cv.ADAPTIVE_THRESH_MEAN_C:阈值是邻近区域的平均值减去常数C。
  • cv.ADAPTIVE_THRESH_GAUSSIAN_C:阈值是邻近区域的高斯加权和减去常数C。

blockSize决定了邻近区域的大小,C是从邻近区域像素的平均值或者加权值减去的一个常数,

下面的代码比较了光照变化的图片的全局阈值和自适应阈值:

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img = cv.imread("sudoku.png",0)
img = cv.medianBlur(img,5)
ret,th1 = cv.threshold(img,127,255,cv.THRESH_BINARY)
th2 = cv.adaptiveThreshold(img,255,cv.ADAPTIVE_THRESH_MEAN_C,\
		CV.THRESH_BINARY,11,2)
th3 = cv.adaptiveThreshold(img,255,cv.ADAPTIVE_THRESH_MEAN_C,\
		cv.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 xrange(4):
	plt.subplot(2,2,i+1),plt.imshow(iamges[i],"gray")
	plt.title(titles[i])
	plt.xticks([]),plt.yticks([])
plt.show()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w0EbWItH-1614070529879)(https://docs.opencv.org/4.5.1/ada_threshold.jpg)]

Otsu’s Binarization

In global thresholding, we used an arbitrary chosen value as a threshold. In contrast, Otsu’s method avoids having to choose a value and determines it automatically.

Consider an image with only two distinct image values (bimodal image), where the histogram would only consist of two peaks. A good threshold would be in the middle of those two values. Similarly, Otsu’s method determines an optimal global threshold value from the image histogram.

In order to do so, the cv.threshold() function is used, where cv.THRESH_OTSU is passed as an extra flag. The threshold value can be chosen arbitrary. The algorithm then finds the optimal threshold value which is returned as the first output.

Check out the example below. The input image is a noisy image. In the first case, global thresholding with a value of 127 is applied. In the second case, Otsu’s thresholding is applied directly. In the third case, the image is first filtered with a 5x5 gaussian kernel to remove the noise, then Otsu thresholding is applied. See how noise filtering improves the result.

在全局阈值中,我们用任意选择的一个值作为阈值。相反,大津方法避免不得不选择一个值并自动决定它。

考虑到一个仅有两个不同图象值的图片(双峰图片),直方图仅包含两个峰。一个好的阈值应该在这两个值的中间。相似地,大津方法从图像直方图决定一个最优的全局阈值。

为了做到这样,cv.threshold()函数被使用,cv.THRESH_OTSU传递一个额外的标志。阈值可以被任意选择。算法然后找到最优阈值,并作为第一个输出返回。

检查下面的例子。输入图像是一个有噪声的图像。在第一个例子中,采用127的全局阈值。在第二个例子中,大津阈值直接应用。在第三个例子中,首先用5x5的高斯核对图像滤波来去除噪声,然后应用大津阈值。看一看噪声滤波如何改善结果的。

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img = cv.imread("nosiy2.png",0)
#global thresholding
ret1,th1 = cv.threshold(img,127,255,cv.THRESH_BINARY)
#Otsu thresholding
ret2,th2 = cv.threshold(img,0,255,cv.THRESH_BINARY+cv.THRESH_OTSU)
#Otsu threshold after gaussian filtering
blur = cv.GaussianBlur(img,(5,5),0)
ret3,th3 = cv.threshold(blur,0,255,cv.THRESH_BINARY+cv.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 thresholding",
			"gaussian filtered image","histogram","otsu thresholding"]
for i in xrange(3):
	plt.subplot(3,3,i3+1),plt.imshow(images[i3],"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()

在这里插入图片描述

How does Otsu’s Binarization work?

This section demonstrates a Python implementation of Otsu’s binarization to show how it actually works. If you are not interested, you can skip this.

Since we are working with bimodal images, Otsu’s algorithm tries to find a threshold value (t) which minimizes the weighted within-class variance given by the relation:
$$

σ 2 w ( t ) = q 1 ( t ) σ 21 ( t ) + q 2 ( t ) σ 22 ( t ) σ2w(t)=q1(t)σ21(t)+q2(t)σ22(t) σ2w(t)=q1(t)σ21(t)+q2(t)σ22(t)
σ2w(t)=q1(t)σ21(t)+q2(t)σ22(t)
w h e r e where where
q1(t)=∑i=1tP(i)&q2(t)=∑i=t+1IP(i)\
μ1(t)=∑i=1tiP(i)q1(t)&μ2(t)=∑i=t+1IiP(i)q2(t)\
σ21(t)=∑i=1t[i−μ1(t)]2P(i)q1(t)&σ22(t)=∑i=t+1I[i−μ2(t)]2P(i)q2(t)\
$$
q1(t)=∑i=1tP(i)&q2(t)=∑i=t+1IP(i)

μ1(t)=∑i=1tiP(i)q1(t)&μ2(t)=∑i=t+1IiP(i)q2(t)

σ21(t)=∑i=1t[i−μ1(t)]2P(i)q1(t)&σ22(t)=∑i=t+1I[i−μ2(t)]2P(i)q2(t)

It actually finds a value of t which lies in between two peaks such that variances to both classes are minimal. It can be simply implemented in Python as follows:

本节展示了大津二值化的Python实现,来展示实际上它是如何工作的。如果你不感兴趣,你可以直接跳过。

因为我们在处理双峰图像,所以大津算法尝试寻找一个阈值t来最小化由关系式给出的加权类内方差:

σ2w(t)=q1(t)σ21(t)+q2(t)σ22(t)

其中:

q1(t)=∑i=1tP(i)&q2(t)=∑i=t+1IP(i)

μ1(t)=∑i=1tiP(i)q1(t)&μ2(t)=∑i=t+1IiP(i)q2(t)

σ21(t)=∑i=1t[i−μ1(t)]2P(i)q1(t)&σ22(t)=∑i=t+1I[i−μ2(t)]2P(i)q2(t)

它实际上找到了两个峰之间的一个值,这样两个类别的差异最小。它可以如下在Python中简单实现:

img = cv.imread("noisy2.png",0)
blur = cv.GaussianBlur(img,(5,5),0)

#find normalized_histogram,and its cumulative distribution function
hist = cv.calcHist([blur],[0],None,[256],[0,256])
hist_norm = hist.ravel()/hist.sum()

bins = np.arange(256)

fn_min = np.inf
thresh = -1

for i in xrange(1,256):
	p1,p2 = np.hsplit(hist_norm,[1])#probabilities
	q1,q2 = Q[i],Q[255]-Q[i]#cum sum of classes
	if q1 < 1.e-6 or q2 < 1.e-6:
		continue
	b1,b2 = np.hsplit(bins,[i])#weights
	
	#finding means and variances
	m1,m2 = np.sum(p1*b1)/q1,np.sum(p2*b2)/q2
	v1,v2 = np.sum(((b1-m1)**2)*p1)/q1,np.sum(((b2-m2)**2)*p2)/q2
	
	#calculates the minimization function
	fn = v1*q1+v2*q2
	if fn < fn_min:
		fn_min = fn
		thresh = i
		
#find ostu threshold value with OpenCV function
ret,otsu = cv.threshold(blur,0,255,cv.THRESH_BINARY+cv.THRESH_OTSU)
print("{},{}".format(thresh,ret))

Additional Resources

  1. Digital Image Processing, Rafael C. Gonzalez

Exercises

  1. There are some optimizations available for Otsu’s binarization. You can search and implement it

    这里有一些可适用于大津二值化优化的方法。你可以搜索并实现它。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值