opencv:图像阈值
1.简单阈值
像素值高于阈值时,我们给这个像素赋予一个新值(可能是白色),否则我们给它赋予另外一种颜色(也许是黑色)。这个函数就是 cv2.threshhold()。
import cv2
import numpy as np
from matplotlib import pyplot as plt
img=cv2.imread('D:/code/opencv/images/1.png')
#函数返回两个参数,一个是retval(暂时不知道是啥),一个就是阈值化后的图像
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)
#超过某个阈值被置0
ret,thresh5=cv2.threshold(img,127,255,cv2.THRESH_TOZERO_INV)
#低于某个值被置0
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.自适应阈值
在前面的部分我们使用是全局阈值,整幅图像采用同一个数作为阈值。当时这种方法并不适应与所有情况,尤其是当同一幅图像上的不同部分的具有不同亮度时。这种情况下我们需要采用自适应阈值。此时的阈值是根据图像上的每一个小区域计算与其对应的阈值。因此在同一幅图像上的不同区域采用的是不同的阈值,从而使我们能在亮度不同的情况下得到更好的结果。
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
img=cv.imread('D:/code/opencv/images/1.png')
# 中值滤波
img = cv.medianBlur(img,5)
ret,th1 = cv.threshold(img,127,255,cv2.THRESH_BINARY)
#用全局阈值
#进行自适应阈值必须对图像进行二值化,我的不进行二值化就报错
gary=cv.cvtColor(img,cv.COLOR_BGR2GRAY)
th2 = cv.adaptiveThreshold(gary,255,cv.ADAPTIVE_THRESH_MEAN_C,cv.THRESH_BINARY,11,2)
#参数图像 高于阈值变成什么 阈值取自相邻区域的平均值 邻域大小 一个常数 阈值为平均值减去这个数
th3 = cv.adaptiveThreshold(gary,255,cv.ADAPTIVE_THRESH_GAUSSIAN_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 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 二值化要做的。简单来说就是对一副双峰图像自动根据其直方图计算出一个阈值。(对于非双峰图像,这种方法得到的结果可能会不理想)。
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
img=cv.imread('D:/code/opencv/images/1.png')
gary=cv.cvtColor(img,cv.COLOR_BGR2GRAY)
# global thresholding 全局阈值
ret1,th1 = cv.threshold(img,127,255,cv.THRESH_BINARY)
# Otsu's thresholding 二值化的图像就会出错 不晓得为啥
ret2,th2 = cv.threshold(gary,0,255,cv.THRESH_BINARY+cv.THRESH_OTSU)
# Otsu's thresholding after Gaussian filtering
#(5,5)为高斯核的大小,0 为标准差
blur = cv.GaussianBlur(gary,(5,5),0) # 阈值一定要设为 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'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()
这图不是双峰的 效果不好