OpenCV-Python-(6)-閾值分割
閾值分割
目的是從灰度影象中分離出目標區域和背景區域,應該使得前景區的平均灰度、背景區灰度的平均值與整幅圖的平均灰度之間的差異最大。影象的二值化使影象中資料量大為減少,從而能凸顯出目標的輪廓。
大於閾值得畫素設為白色(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()