阈值分割
目的是从灰度图像中分离出目标区域和背景区域,应该使得前景区的平均灰度、背景区灰度的平均值与整幅图的平均灰度之间的差异最大。图像的二值化使图像中数据量大为减少,从而能凸显出目标的轮廓。
大于阈值得像素设为白色(255),小于&等于阈值得像素设为黑色(0)(也可以反过来)。
- retval, dst = cv2.threshold( src, thresh, maxval, type[, dst] )
- THRESH_TRIANGLE
- THRESH_OTSU
- cv2.adaptiveThreshold(src, dst, maxValue, adaptiveMethod, teresholdType, blocksize, C)
- threshTwoPeaks(image)
- threshEntropy(image)
1.全局阈值分割
降噪,过滤很小或很大像素值的图像点。
大于阈值得像素设为白色,小于&等于阈值得像素设为黑色(也可以反过来)。
retval, dst = cv2.threshold( src, thresh, maxval, type[, dst] )
retval:返回计算后的阈值
src:原图像,单通道矩阵,CV_8U&CV_32F
dst:结果图像
thresh:当前阈值
maxVal:最大阈值,一般为255
thresholdType:阈值类型,主要有下面几种:
- THRESH_BINARY:二进制阈值。大于阈值的像素=maxVal,小于&等于阈值的像素=0(value>threshold?255:0)
- THRESH_BINARY_INV:反二进制阈值。大于阈值的像素=0,小于&等于阈值的像素=maxVal (value>threshold?0:255)
- THRESH_TRUNC:截断阈值。大于阈值的像素=阈值,小于&等于阈值的像素=不变 (value>threshold?threshold:value)
- THRESH_TOZERO:阈值化为0。大于阈值的像素=不变,小于&等于阈值得像素=0 (value>threshold?value:0)
- THRESH_TOZERO_INV:反阈值化为0。大于阈值的像素=0,小于&等于阈值得像素=不变 (value>threshold?0:value)
注:THRESH_OTSU和THRESH_TRIANGLE是作为优化算法配合
THRESH_BINARY、THRESH_BINARY_INV、THRESH_TRUNC、THRESH_TOZERO&THRESH_TOZERO_INV来使用的。
当使用了THRESH_OTSU和THRESH_TRIANGLE两个标志时,输入图像必须为单通道。
#全局阈值分割
#threshold(src(单通道矩阵,CV_8U&CV_32F), dst, thresh(阈值), maxVal(在图像二值化显示时,一般=为255), type)
#大于阈值得像素=maxVal,小于&等于阈值得像素=0 (type=THRESH_BINARY)
#大于阈值得像素=0,小于&等于阈值得像素=maxVal (type=THRESH_BINARY_INV)
#当(type=THRESH_OTSU & type=THRESH_TRIANGLE(3.x新特性))时,会自动计算阈值。
#当(type=THRESH_OTSU + THRESH_BINARY)时,即先用THRESH_OTSU自动计算出阈值,然后利用该阈值采用THRESH_BINARY规则(默认)。
import cv2
import numpy as np
src = np.array([[123,234,68],
[33,51,17],
[48,98,234],
[129,89,27],
[45,167,134]],np.uint8)
#手动设置阈值
the = 150
maxval = 255
the, dst = cv2.threshold(src, the, maxval, cv2.THRESH_BINARY_INV)
print (the)
print (dst)
#Otsu阈值处理
otsuThe = 0
otsuThe, dst_Otsu = cv2.threshold(src, otsuThe, maxval, cv2.THRESH_OTSU)
print (otsuThe)
print (dst_Otsu)
#TRIANGLE阈值处理
triThe = 0
triThe, dst_tri = cv2.threshold(src, triThe, maxval, cv2.THRESH_TRIANGLE + cv2.THRESH_BINARY_INV)
print (triThe)
print (dst_tri)
2.THRESH_TRIANGLE优化算法
当(type=THRESH_TRIANCLE + THRESH_BINARY)时,即先用THRESH_TRIANGLE自动计算出阈值,然后利用该阈值采用THRESH_BINARY规则(默认)。
import cv2
#TRIANGLE阈值处理
src = cv2.imread(r'C:\Users\x\Desktop\OpenCV-Pic\6\img7.jpg', cv2.IMREAD_GRAYSCALE)
triThe = 0
maxval = 255
triThe, dst_tri = cv2.threshold(src, triThe, maxval, cv2.THRESH_TRIANGLE + cv2.THRESH_BINARY)
triThe1, dst_tri1 = cv2.threshold(src, triThe, maxval, cv2.THRESH_TRIANGLE + cv2.THRESH_BINARY_INV)
print (triThe)
print (triThe1)
cv2.imshow("image", src)
cv2.imshow('thresh_out', dst_tri)
cv2.imshow('thresh_out1', dst_tri1)
cv2.waitKey(0)
cv2.destroyAllWindows()
3.THRESH_OTSU优化算法
Otsu提出最大方差法
在判别分析最小二乘法原理的基础上推导而来
import cv2
#TRIANGLE阈值处理
src = cv2.imread(r'C:\Users\x\Desktop\OpenCV-Pic\6\img7.jpg', cv2.IMREAD_GRAYSCALE)
triThe = 0
maxval = 255
triThe, dst_tri = cv2.threshold(src, triThe, maxval, cv2.THRESH_OTSU + cv2.THRESH_BINARY)
triThe1, dst_tri1 = cv2.threshold(src, triThe, maxval, cv2.THRESH_OTSU + cv2.THRESH_BINARY_INV)
print (triThe)
print (triThe1)
cv2.imshow("image", src)
cv2.imshow('thresh_out', dst_tri)
cv2.imshow('thresh_out1', dst_tri1)
cv2.waitKey(0)
cv2.destroyAllWindows()
4.自适应阈值分割
区域(局部)自适应二值化 计算大概过程是为每一个象素点单独计算的阈值,即每个像素点的阈值都是不同的,就是将该像素点周围B*B区域内的像素加权平均,然后减去一个常数C,从而得到该点的阈值。
针对每个位置的灰度值设置一个对应的阈值,而该位置的阈值的设置和其邻域有必然的关系。
可以使用平滑处理后的输出结果作为每个像素设置阈值的参考值。
一般,平滑算子的宽度必须大于被识别的物体的宽度。
优点:
- 在于每个像素位置处的二值化阈值不是固定不变的,而是由其周围邻域像素的分布来决定的。
- 亮度较高的图像区域的二值化阈值通常会较高,而亮度较低的图像区域的二值化阈值则会相适应地变小。
- 不同亮度、对比度、纹理的局部图像区域将会拥有相对应的局部二值化阈值。
cv2.adaptiveThreshold(src, dst, maxValue, adaptiveMethod, teresholdType, blocksize, C)
src:要二值化的灰度图
dst:二值化后的图
maxValue:一般取255
adaptiveMethod:块计算的方法
- ADAPTIVE_THRESH_MEAN_C(均值平滑),为局部邻域块的平均值。该算法是先求出块中的均值,再减去常数C。
- ADAPTIVE_THRESH_GAUSSIAN_C(高斯平滑),为局部邻域块的高斯加权和。该算法是在区域中(x,y)周围的像素根据高斯函数按照他们离中心点的距离进行加权计算, 再减去常数C。
teresholdTypt:二值化类型
- THRESH_BINARY
- THRESH_BINARY_INV
blocksize:平滑算子尺寸为奇数
C:系数
import cv2
img = cv2.imread(r'C:\Users\x\Desktop\OpenCV-Pic\6\img4.jpg', cv2.IMREAD_GRAYSCALE)
dst = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 43, 0.15)
cv2.imshow("image", img)
cv2.imshow('thresh_out', dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
5.直方图技术法
直方图技术法就是首先找到这两个峰值,然后取这两个峰值之间得波谷位置对应得灰度值,就是所要得阈值。
在有明显波峰的图像的阈值处理效果好。
import cv2
import numpy as np
def caleGrayHist(image):
#灰度图像的高、宽
rows, cols = image.shape
#存储灰度直方图
grayHist = np.zeros([256], np.uint64) #图像的灰度级范围是0~255
for r in range(rows):
for c in range(cols):
grayHist[image[r][c]] += 1
return grayHist
def threshTwoPeaks(image):
#计算灰度直方图
histogram = caleGrayHist(image)
#找到灰度直方图的最大峰值对应得灰度值
maxLoc = np.where(histogram==np.max(histogram))
firstPeak = maxLoc[0][0] #取第一个最大的灰度值
#寻找灰度直方图的第二个峰值对应得灰度值
measureDists = np.zeros([256], np.float32)
for k in range(256):
measureDists[k] = pow(k-firstPeak,2)*histogram[k]
maxLoc2 = np.where(measureDists==np.max(measureDists))
secondPeak = maxLoc2[0][0]
#找到两个峰值之间的最小值对应的灰度值,作为阈值
thresh = 0
if firstPeak > secondPeak: #第一个峰值在第二个峰值右侧
temp = histogram[int(secondPeak):int(firstPeak)]
minLoc = np.where(temp==np.min(temp))
thresh = secondPeak + minLoc[0][0] + 1 #有多个波谷取左侧的波谷
else:
temp = histogram[int(firstPeak):int(secondPeak)]
minLoc = np.where(temp==np.min(temp))
thresh = firstPeak + minLoc[0][0] + 1
#找到阈值后进行阈值处理,得到二值图
threshImage_out = image.copy()
threshImage_out[threshImage_out > thresh] = 255
threshImage_out[threshImage_out <= thresh] = 0
return (thresh, threshImage_out)
#THRESH_TRIANGLE与直方图技术法类似(效果更好)
img = cv2.imread(r'C:\Users\x\Desktop\OpenCV-Pic\6\img7.jpg', cv2.IMREAD_GRAYSCALE)
the, dst = threshTwoPeaks(img)
the1 = 0
maxval = 255
the1, dst1 = cv2.threshold(img, the1, maxval, cv2.THRESH_TRIANGLE + cv2.THRESH_BINARY)
print('The thresh is :', the)
print('The thresh1 is :', the1)
cv2.imshow("image", img)
cv2.imshow('thresh_out', dst)
cv2.imshow('thresh_out1', dst1)
cv2.waitKey(0)
cv2.destroyAllWindows()
6.熵算法
import cv2
import math
import numpy as np
def caleGrayHist(image):
#灰度图像的高、宽
rows, cols = image.shape
#存储灰度直方图
grayHist = np.zeros([256], np.uint64) #图像的灰度级范围是0~255
for r in range(rows):
for c in range(cols):
grayHist[image[r][c]] +=1
return grayHist
def threshEntropy(image):
rows, cols = image.shape
#求灰度直方图
grayHist = caleGrayHist(image)
#归一化灰度直方图,即概率直方图
normGrayHist = grayHist/float(rows*cols)
#第一步:计算累加直方图,也成为零阶累矩阵
zeroCumuMoment = np.zeros([256], np.float32)
for k in range(256):
if k == 0 :
zeroCumuMoment[k] = normGrayHist[k]
else:
zeroCumuMoment[k] = zeroCumuMoment[k-1] + normGrayHist[k]
#第二步:计算各个灰度级的熵
entropy = np.zeros([256], np.float32)
for k in range(256):
if k == 0 :
if normGrayHist[k] == 0 :
entropy[k] = 0
else:
entropy[k] = - normGrayHist[k] * math.log10(normGrayHist[k])
else:
if normGrayHist[k] == 0 :
entropy[k] = entropy[k-1]
else:
entropy[k] = entropy[k-1] - normGrayHist[k] * math.log10(normGrayHist[k])
#第三步:找阈值
fT = np.zeros([256], np.float32)
ft1, ft2 = 0.0, 0.0
totalEntropy = entropy[255]
for k in range(255):
#找最大值
maxFront = np.max(normGrayHist[0:k+1])
maxBack = np.max(normGrayHist[k+1:256])
if maxFront==0 or zeroCumuMoment[k]==0 or maxFront==1 or zeroCumuMoment[k]==1 or totalEntropy==0 :
ft1 = 0
else:
ft1 = entropy[k]/totalEntropy*(math.log10(zeroCumuMoment[k])/math.log10(maxFront))
if maxBack==0 or 1-zeroCumuMoment[k]==0 or maxBack==1 or 1-zeroCumuMoment[k]==1 :
ft2 = 0
else:
if totalEntropy == 0 :
ft2 = (math.log10(1-zeroCumuMoment[k])/math.log10(maxBack))
else:
ft2 = (1-entropy[k]/totalEntropy)*(math.log10(1-zeroCumuMoment[k])/math.log10(maxBack))
fT[k] = ft1 + ft2
#找到最大值索引
threshLoc = np.where(fT==np.max(fT))
thresh = threshLoc[0][0]
#阈值处理
threshold = np.copy(image)
threshold[threshold > thresh] = 255
threshold[threshold <= thresh] = 0
return (thresh, threshold)
img = cv2.imread(r'C:\Users\x\Desktop\OpenCV-Pic\6\img8.jpg', cv2.IMREAD_GRAYSCALE)
the, dst = threshEntropy(img)
the1 = 0
maxval = 255
the1, dst1 = cv2.threshold(img, the1, maxval, cv2.THRESH_TRIANGLE + cv2.THRESH_BINARY)
print('The thresh is :', the)
print('The thresh1 is :', the1)
cv2.imshow("image", img)
cv2.imshow('thresh_out', dst)
cv2.imshow('thresh_out1', dst1)
cv2.waitKey(0)
cv2.destroyAllWindows()
测试图片