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

猜你喜欢

转载自blog.csdn.net/qq_40755643/article/details/84066750
今日推荐