日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

OpenCV与图像处理学习七——传统图像分割之阈值法(固定阈值、自适应阈值、大津阈值)

發布時間:2024/7/23 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 OpenCV与图像处理学习七——传统图像分割之阈值法(固定阈值、自适应阈值、大津阈值) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

OpenCV與圖像處理學習七——傳統圖像分割之閾值法(固定閾值、自適應閾值、大津閾值)

  • 一、固定閾值圖像分割
    • 1.1 直方圖雙峰法
    • 1.2 OpenCV中的固定閾值分割
  • 二、自動閾值圖像分割
    • 2.1 自適應閾值法
    • 2.2 迭代法閾值分割
    • 2.3 Otsu大津閾值法

前面的筆記介紹了一些OpenCV基本的圖像處理,后面將學習使用OpenCV的傳統的圖像分割方法,這次筆記的內容是閾值法進行圖像分割。

圖像分割是指將圖像分成若干具有相似性質的區域的過程,主要有基于閾值、基于區域、基于邊緣、基于聚類、基于圖論和基于深度學習的圖像分割方法等。圖像分割分為語義分割實例分割。下圖是一個實例分割的例子,與語義分割不同的地方在于,它能將同類別的物體的每個個體都區分開,如下圖中每個人的輪廓都被分割開:


分割的原則就是使劃分后的子圖在內部保持相似度最大,而子圖之間的相似度最小,將G = (V, E) 分成兩個子集A,B,使得:

一、固定閾值圖像分割

即設定一個固定的閾值,整張圖片的每個像素的像素值都與該值進行比較,若小于該閾值則將像素值改為一個固定的值(常用0),若大于該閾值則將像素值改為另一個固定的值(常用255),則可以將圖像進行二值分割,得到一張二值圖。

1.1 直方圖雙峰法

六十年代中期提出的直方圖雙峰法(也稱mode法)是典型的全局單閾值分割方法。

基本思想:假設圖像中有明顯的目標和背景,則其灰度直方圖呈雙峰分布,當灰度級直方圖具有雙峰特性時選取兩峰之間的谷對應的灰度級作為閾值,大于閾值的作為前景,小于的作為背景。

缺點:對圖像的要求太高,很多圖像的直方圖并不滿足雙峰的分布。

1.2 OpenCV中的固定閾值分割

在OpenCV中的函數:

retval, dst = cv2.threshold( src, thresh, maxval, type[, dst] )

參數:

  • src:輸入圖像,單通道或四通道圖像。
  • thresh:設定的固定閾值。
  • maxval:當type參數設置為THRESH_BINARY時,表示像素值大于閾值時設置的值,或設置為THRESH_BINARY_INV時,表示像素值小于閾值時設置的值。
  • type:閾值類型,如下圖所示,前五種類型是基本類型,最后兩種(大津閾值和三角形閾值)與前面的基本類型結合可以實現特殊的閾值分割:
  • 這里也需要注意一下這個函數的輸出:

  • retval:第一個參數返回的是閾值,在使用五種基本類型時就等于你設置的閾值,而在使用大津閾值法和三角形閾值法時將會得到它計算出來的閾值,一般用的不多。
  • dst:第二個參數返回的才是分割之后的圖像。
  • 下面看幾個例子:

    # 加載opencv和matplotlib import cv2 import matplotlib.pyplot as plt# 灰度圖讀入 img = cv2.imread('./image/thresh.png', 0) threshold = 127 # 閾值分割 ret, th = cv2.threshold(img, threshold, 255, cv2.THRESH_BINARY) print(ret)cv2.imshow('Original', img) cv2.imshow('thresh', th) cv2.waitKey(0) cv2.destroyAllWindows()

    結果如下:

    返回的第一個參數就是設置的閾值:

    127.0

    即像素值低于127的都被賦予0,高于的都被賦予255,得到一張二值化的圖像。

    再來看一下五種基本的閾值分割方法的區別

    # 導入第三方包 import cv2 from matplotlib import pyplot as plt # opencv讀取圖像 img = cv2.imread('./image/person.png',0) # 5種閾值法圖像分割 ret1, thresh1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY) ret2, thresh2 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV) ret3, thresh3 = cv2.threshold(img, 127, 255, cv2.THRESH_TRUNC) ret4, thresh4 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO) ret5, thresh5 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO_INV)images = [img, thresh1, thresh2, thresh3, thresh4, thresh5] # 使用for循環進行遍歷,matplotlib進行顯示 for i in range(6):plt.subplot(2, 3, i+1)plt.imshow(images[i], cmap='gray')plt.xticks([])plt.yticks([])plt.suptitle('fixed threshold') plt.show()

    結果為:

    注意:除了前兩種方法是二值化圖像,后三種并不是,因為它們會保留一部分的原像素值。

    如果這個圖不夠直觀,將輸入圖像換成上一個例子里的漸變灰度圖,結果如下:

    二、自動閾值圖像分割

    2.1 自適應閾值法

    很明顯,設置一個固定閾值對全局像素進行分割是不合理的,如果這張圖片的光照角度不好,一邊比較亮一邊比較暗,但是我們想分割圖里的細節,只用一個固定閾值的話很可能會出現下面的結果:

    而自適應閾值分割則將圖像分成很多個小塊(region),對每個小塊單獨計算其閾值,然后用這個計算得到的閾值對該小塊進行分割,這樣的好處是,即使受到光照影響,某一塊較暗或較亮,但是可以單獨計算這一塊的合理閾值來進行分割而不用使用全局的固定閾值,換句話說,亮的小塊對應的閾值較大,暗的小塊對應的閾值較小,從而可以達到很好的分割效果。

    函數:

    dst = cv2.adaptiveThreshold( src, maxValue, adaptiveMethod, thresholdType, blockSize, C[, dst] )

    參數:

  • src:輸入圖像,只能是單通道灰度圖。
  • maxValue:最大閾值,即小塊計算的閾值不能超過這個值,一般設置為255.
  • adaptiveMethod:計算小塊閾值的方法,包括cv2.ADAPTIVE_THRESH_MEAN_C和cv.ADAPTIVE_THRESH_GAUSSIAN_C,即求小塊內的均值或高斯加權求和:
  • thresholdType:閾值方法,這里只能是THRESH_BINARY或THRESH_BINARY_INV,即
  • blockSize:小塊的尺寸,如11就是11×11。
  • C:最終閾值等于小區域計算出的閾值再減去這個常數。
  • 看一下剛剛用固定閾值分割效果很差的那個圖用自適應閾值來分割的效果:

    # 自適應閾值與固定閾值對比 import cv2 import matplotlib.pyplot as plt img = cv2.imread('./image/paper2.png', 0)# 固定閾值 ret, th1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY) # 自適應閾值 th2 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 4) th3 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 4) # 全局閾值,均值自適應,高斯加權自適應對比 titles = ['Original', 'Global(v = 127)', 'Adaptive Mean', 'Adaptive Gaussian'] 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], fontsize=8)plt.xticks([]), plt.yticks([]) plt.show()


    可以看出效果還是很好的,相比之下,高斯方法的自適應閾值能獲得更好的效果,其噪點更少。

    2.2 迭代法閾值分割

    步驟:

  • 求出圖像的最大灰度值和最小灰度值,分別記為Zmax和Zmin,另初始閾值為T0 = (Zmax + Zmin) / 2。
  • 根據閾值Tk將圖像分割為前景和背景,分別求出兩者的平均灰度值Zo和Zb。
  • 求出新的閾值Tk+1 = (Zo + Zb) / 2。
  • 若Tk == Tk+1,則即為所求的閾值,否則轉到步驟2繼續迭代。
  • 使用計算后的閾值進行閾值分割。
  • 其實迭代法就是將固定閾值分割里手動給定閾值改為了迭代計算閾值,可以適用的范圍更多一些,但是本質還是固定閾值變換。

    看個例子:

    import cv2 import numpy as np import matplotlib.pyplot as plt import matplotlib.cm as cmdef best_thresh(img):# step 1: 設置初始閾值img_array = np.array(img).astype(np.float32) # 轉化成數組I = img_arrayzmax = np.max(I)zmin = np.min(I)tk = (zmax+zmin)/2# step 2: 根據閾值將圖像進行分割為前景和背景,分別求出兩者的平均灰度zo和zbb = 1m, n = I.shape;while b == 0:ifg = 0ibg = 0fnum = 0bnum = 0for i in range(1, m):for j in range(1, n):tmp = I(i, j)if tmp >= tk:ifg = ifg + 1fnum = fnum + int(tmp) # 前景像素的個數以及像素值的總和else:ibg = ibg+1bnum = bnum + int(tmp) # 背景像素的個數以及像素值的總和# step 3: 計算前景和背景的新平均值zo = int(fnum / ifg)zb = int(bnum / ibg)# step 4: 比較tk是否等于新平均值if tk == int((zo+zb) / 2):b = 0else:tk = int((zo+zb)/2)# step 5: 返回的就是迭代計算后的閾值return tkimg = cv2.imread("./image/bird.png") img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) img = cv2.resize(gray, (200, 200)) # 大小 yvzhi = best_thresh(img) ret1, th1 = cv2.threshold(img, yvzhi, 255, cv2.THRESH_BINARY) print(ret1) plt.imshow(th1, cmap=cm.gray) plt.show()

    結果為:

    2.3 Otsu大津閾值法

    大津法:也叫最大類間方差法,1979年日本學者大津提出,是一種基于全局閾值的自適應方法。

    圖像分為前景和背景,當取最佳閾值時,兩部分之間的差別應該是最大的,衡量差別的方法為最大類間方差。

    直方圖有兩個峰值的圖像,用大津法求得的閾值近似為谷底,如下圖所示,即代替了手動輸入閾值。

    大津法中類間方差是需要最大化的目標函數,那么它的定義如下圖所示:

    在OpenCV中大津閾值法只是在固定閾值法的函數cv2.threshold的閾值方法type這個參數后加上cv2.THRESH_OTSU,同時將參數2thresh忽視(設置多少無所謂,一般使用0),例如:

    ret2, th2 = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

    看一個例子:

    import cv2 from matplotlib import pyplot as pltimg = cv2.imread('./image/noisy.png', 0) # 固定閾值法 ret1, th1 = cv2.threshold(img, 100, 255, cv2.THRESH_BINARY) # Otsu閾值法 ret2, th2 = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)# 先進行高斯濾波,再使用Otsu閾值法 blur = cv2.GaussianBlur(img, (5, 5), 0) ret3, th3 = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) images = [img, 0, th1, img, 0, th2, blur, 0, th3] titles = ['Original', 'Histogram', 'Global(v=100)','Original', 'Histogram', "Otsu's",'Gaussian filtered Image', 'Histogram', "Otsu's"]for i in range(3):# 繪制原圖plt.subplot(3, 3, i * 3 + 1)plt.imshow(images[i * 3], 'gray')plt.title(titles[i * 3], fontsize=8)plt.xticks([]), plt.yticks([])# 繪制直方圖plt.hist, ravel函數將數組降成一維plt.subplot(3, 3, i * 3 + 2)plt.hist(images[i * 3].ravel(), 256)plt.title(titles[i * 3 + 1], fontsize=8)plt.xticks([]), plt.yticks([])# 繪制閾值圖plt.subplot(3, 3, i * 3 + 3)plt.imshow(images[i * 3 + 2], 'gray')plt.title(titles[i * 3 + 2], fontsize=8)plt.xticks([]), plt.yticks([]) plt.show()

    這里比較了固定閾值法、大津閾值法和加上高斯濾波的大津閾值法,效果如下所示:

    相比之下,高斯濾波加大津閾值法的效果是最好的,也是實際使用中最常用的。

    以上就是基于閾值的傳統圖像分割中常用的幾種閾值分割方法。

    總結

    以上是生活随笔為你收集整理的OpenCV与图像处理学习七——传统图像分割之阈值法(固定阈值、自适应阈值、大津阈值)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。