1. 程式人生 > >OpenCV-Python-(6)-閾值分割

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,從而得到該點的閾值

針對每個位置的灰度值設定一個對應的閾值,而該位置的閾值的設定和其鄰域有必然的關係。
可以使用平滑處理後的輸出結果作為每個畫素設定閾值的參考值。
一般,平滑運算元的寬度必須大於被識別的物體的寬度。

優點:

  1. 在於每個畫素位置處的二值化閾值不是固定不變的,而是由其周圍鄰域畫素的分佈來決定的。
  2. 亮度較高的影象區域的二值化閾值通常會較高,而亮度較低的影象區域的二值化閾值則會相適應地變小。
  3. 不同亮度、對比度、紋理的區域性影象區域將會擁有相對應的區域性二值化閾值。

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()