[OpenCV官方教程中文版-段力輝譯]-圖像的二值化-cv2.threshold()、cv2.adaptiveThreshold()和Otsu’s 二值化

1. 什么是圖像的二值化

  將圖像上的[像素](https://baike.baidu.com/item/%E5%83%8F%E7%B4%A0/95084)點(diǎn)的[灰度值](https://baike.baidu.com/item/%E7%81%B0%E5%BA%A6%E5%80%BC/10259111)設(shè)置為0或255,也就是將整個(gè)圖像呈現(xiàn)出明顯的黑白效果的過(guò)程。

  圖像的二值化使圖像中數(shù)據(jù)量大為減少,從而能凸顯出目標(biāo)的輪廓剃浇。

2. 實(shí)現(xiàn)函數(shù)- cv2.threshold()、cv2.adaptiveThreshold()

2.1 全局閾值-cv2.threshold()

  • 參數(shù)如下:cv2.threshold(src, thresh, maxval, type)
  • 參數(shù)說(shuō)明: 1. src 源圖像,必須是單通道 2. thresh 分類閾值 3. maxval 高于(低于)閾值時(shí)賦予的新值 4. type 閾值處理模式
  • 返回值: 1.retval 得到的閾值 2. dst 閾值化后的圖像
  • 閾值處理模式:cv2.THRESH_BINARY、cv2.THRESH_BINARY_INV偿渡、cv2.THRESH_TRUNC臼寄、
    cv2.THRESH_TOZERO、cv2.THRESH_TOZERO_INV,具體說(shuō)明如下:
    - 1. cv2.THRESH—BINARY
    如果像素值大于閾值溜宽,像素值就會(huì)被設(shè)為參數(shù)3; 小于等于閾值吉拳,設(shè)定為0
    - 2. cv2.THRESH_BINARY_INV
    這個(gè)是上面一種情況的反轉(zhuǎn): 如果像素值大于閾值,像素值為0; 小于等于閾值适揉,設(shè)定為參數(shù)3
    - 3. cv2.THRESH_TRUNC
    如果像素大于閾值留攒,設(shè)定為閾值;小于等于閾值,保持原像素值
    - 4. cv2.THRESH_TOZERO
    大于閾值嫉嘀,保持原像素值; 小于等于炼邀,設(shè)定為0
    - 5. cv2.THRESH_TOZERO_INV
    與上一種相反:大于閾值,設(shè)定為0;小于等于剪侮,保持原像素值

代碼如下:

def threshold_simple(image):
    img = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
    ret, thresh1 = cv.threshold(img, 127, 255, cv.THRESH_BINARY)
    ret, thresh2 = cv.threshold(img, 127, 255, cv.THRESH_BINARY_INV)
    ret, thresh3 = cv.threshold(img, 127, 255, cv.THRESH_TRUNC)
    ret, thresh4 = cv.threshold(img, 127, 255, cv.THRESH_TOZERO)
    ret, thresh5 = cv.threshold(img, 127, 255, cv.THRESH_TOZERO_INV)
    titles = ['Original Image', 'BINARY', 'BINARY_INV', 'TRUNC', 'TOZERO', 'TOZERO_INV']
    images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]
 
    for i in range(6):
        plt.subplot(2, 3, i + 1), plt.imshow(images[i], 'gray')  # 將圖像按2x3鋪開
        plt.title(titles[i])
        plt.xticks([]), plt.yticks([])
 
    plt.show()

2.2 自適應(yīng)閾值 -cv2.adaptiveThreshold()

自適應(yīng)閾值可以看成一種局部性的閾值拭宁,通過(guò)規(guī)定一個(gè)區(qū)域大小,比較這個(gè)點(diǎn)與區(qū)域大小里面像素點(diǎn)的平均值(或者其他特征)的大小關(guān)系確定這個(gè)像素點(diǎn)是屬于黑或者白(如果是二值情況)瓣俯。該函數(shù)需要填6個(gè)參數(shù):

  • 第一個(gè)原始圖像
  • 第二個(gè)像素值上限
  • 第三個(gè)自適應(yīng)方法Adaptive Method:
  • — cv2.ADAPTIVE_THRESH_MEAN_C :領(lǐng)域內(nèi)均值
  • —cv2.ADAPTIVE_THRESH_GAUSSIAN_C :領(lǐng)域內(nèi)像素點(diǎn)加權(quán)和杰标,權(quán)重為一個(gè)高斯窗口
  • 第四個(gè)值的賦值方法:只有cv2.THRESH_BINARY 和cv2.THRESH_BINARY_INV
  • 第五個(gè)Block size:規(guī)定領(lǐng)域大小(一個(gè)正方形的領(lǐng)域)彩匕,計(jì)算鄰域時(shí)的領(lǐng)鄰域大小腔剂,必須為奇數(shù);當(dāng)blockSize越大,參與計(jì)算閾值的區(qū)域也越大驼仪,細(xì)節(jié)輪廓就變得越少掸犬,整體輪廓越粗越明顯
  • 第六個(gè)常數(shù)C,閾值等于均值或者加權(quán)值減去這個(gè)常數(shù),得到的就是最終閾值绪爸。當(dāng)C越大湾碎,每個(gè)像素點(diǎn)的N*N鄰域計(jì)算出的閾值就越小,中心點(diǎn)大于這個(gè)閾值的可能性也就越大奠货,設(shè)置成255的概率就越大介褥,整體圖像白色像素就越多,反之亦然仇味。
    這種方法理論上得到的效果更好,相當(dāng)于在動(dòng)態(tài)自適應(yīng)的調(diào)整屬于自己像素點(diǎn)的閾值雹顺,而不是整幅圖像都用一個(gè)閾值丹墨。

代碼如下:

def threshold_adaptive(image):
    img = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
    # 中值濾波
    img = cv.medianBlur(img, 5)
 
    ret, th1 = cv.threshold(img, 127, 255, cv.THRESH_BINARY)
    # 11 為 Block size, 2 為 C 值
    th2 = cv.adaptiveThreshold(img, 255, cv.ADAPTIVE_THRESH_MEAN_C, cv.THRESH_BINARY, 11, 2)
    th3 = cv.adaptiveThreshold(img,255,cv.ADAPTIVE_THRESH_GAUSSIAN_C, cv.THRESH_BINARY, 11, 2)
 
    titles = ['Original Image', 'Global Threshold (v = 127)', 'Adaptive Mean Threshold', 'Adaptive Gaussian Threshold']
    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])
        plt.xticks([]), plt.yticks([])
 
    plt.show()

import cv2
import numpy as np
 
blocksize = 3
C=0
def adaptive_demo(gray, blocksize, C):
    binary = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, blocksize, C)
    # binary = cv2.GaussianBlur(binary, (15,15), 0)
    cv2.imshow('binary', binary)
 
def C_changed(value):
    global gray
    global blocksize
    global C
    C = value - 30
    print('C:', C)
    adaptive_demo(gray, blocksize, C)
 
def blocksize_changed(value):
    global gray
    global blocksize
    global C
    blocksize = 2 * value + 1
 
    print('blocksize:', blocksize)
    adaptive_demo(gray, blocksize, C)
 
if __name__ == "__main__":
    image_path = './img/1.jpg'
    img = cv2.imread(image_path)
 
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
 
    adaptive_demo(gray, 3, 0)
    cv2.createTrackbar('C', 'binary',0, 60, C_changed)
    cv2.createTrackbar('blocksize', 'binary',1, 20, blocksize_changed)
 
    cv2.waitKey(0)

在使用全局閾值時(shí),隨便給了一個(gè)數(shù)來(lái)做閾值嬉愧,那怎么知道選取的這個(gè)數(shù)的好壞呢贩挣?答案就是不停的嘗試。如果是一副雙峰圖像(簡(jiǎn)單來(lái)說(shuō)雙峰圖像是指圖像直方圖中存在兩個(gè)峰)呢?我們豈不是應(yīng)該在兩個(gè)峰 之間的峰谷選一個(gè)值作為閾值王财?這就是 Otsu 二值化要做的卵迂。簡(jiǎn)單來(lái)說(shuō)就是對(duì) 一副雙峰圖像自動(dòng)根據(jù)其直方圖計(jì)算出一個(gè)閾值。(對(duì)于非雙峰圖像绒净,這種方法得到的結(jié)果可能會(huì)不理想)见咒。

這里用到到的函數(shù)還是 cv2.threshold(),但是需要多傳入一個(gè)參數(shù) (?ag):cv2.THRESH_OTSU挂疆。這時(shí)要把閾值設(shè)為 0改览。然后算法會(huì)找到最優(yōu)閾值,這個(gè)最優(yōu)閾值就是返回值 retVal缤言。如果不使用 Otsu 二值化宝当,返回的 retVal 值與設(shè)定的閾值相等。

下面的例子中胆萧,輸入圖像是一副帶有噪聲的圖像庆揩。第一種方法,設(shè)127為全局閾值跌穗。第二種方法订晌,我們直接使用 Otsu 二值化。第三種方法瞻离,我 們首先使用一個(gè) 5x5 的高斯核除去噪音腾仅,然后再使用 Otsu 二值化√桌看看噪音去除對(duì)結(jié)果的影響有多大推励。


import cv2
import numpy as np
from matplotlib import pyplot as plt
 
 
img = cv2.imread('F:/OTSnoise.png',0)
#全局閾值
ret1, th1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY)
#OTS閾值
ret2, th2 = cv2.threshold(img,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
#(5,5)為高斯核的大小,0為標(biāo)準(zhǔn)差
blur = cv2.GaussianBlur(img,(5,5),0)
#閾值一定設(shè)為0
ret3, th3 = cv2.threshold(blur, 0, 255, cv2.THRESH_OTSU)
 
images = [img, 0, th1,
          img, 0, th2,
          blur, 0, th3]
 
titles = ['Original Noisy Image','Histogram','Global Thresholding (v=127)',
          'Original Noisy Image','Histogram',"Otsu's Thresholding",
          'Gaussian filtered Image','Histogtam',"Otus's Thresholding"]
 
for i in xrange(3):
    plt.subplot(3,3,i*3+1),plt.imshow(images[i*3],'gray')
    plt.title(titles[i*3]),plt.xticks([]),plt.yticks([])
    plt.subplot(3,3,i*3+2),plt.hist(images[i*3].ravel(),256)#plt.hist是畫直方圖
    plt.title(titles[i*3+1]),plt.xticks([]),plt.yticks([])
    plt.subplot(3,3,i*3+3),plt.imshow(images[i*3+2],'gray')
    plt.title(titles[i*3+2]),plt.xticks([]),plt.yticks([])
 
plt.show()
image.png

Otsu’s 二值化是如何工作的肉迫?

image.png
import cv2
import numpy as np
img = cv2.imread('noisy2.png',0)
blur = cv2.GaussianBlur(img,(5,5),0)
# find normalized_histogram, and its cumulative distribution function
# 計(jì)算歸一化直方圖
#CalcHist(image, accumulate=0, mask=NULL)
hist = cv2.calcHist([blur],[0],None,[256],[0,256])
hist_norm = hist.ravel()/hist.max()
Q = hist_norm.cumsum()
bins = np.arange(256)
fn_min = np.inf
thresh = -1
for i in xrange(1,256):
    p1,p2 = np.hsplit(hist_norm,[i]) # probabilities
    q1,q2 = Q[i],Q[255]-Q[i] # cum sum of classes 
    b1,b2 = np.hsplit(bins,[i]) # weights
    # finding means and variances
    m1,m2 = np.sum(p1*b1)/q1, np.sum(p2*b2)/q2
    v1,v2 = np.sum(((b1-m1)**2)*p1)/q1,np.sum(((b2-m2)**2)*p2)/q2
    # calculates the minimization function
    fn = v1*q1 + v2*q2
    if fn < fn_min:
        fn_min = fn
        thresh = i
# find otsu's threshold value with OpenCV function
ret, otsu = cv2.threshold(blur,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
print thresh,ret
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末验辞,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子喊衫,更是在濱河造成了極大的恐慌跌造,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,252評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件族购,死亡現(xiàn)場(chǎng)離奇詭異壳贪,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)寝杖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門违施,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人瑟幕,你說(shuō)我怎么就攤上這事磕蒲×袅剩” “怎么了?”我有些...
    開封第一講書人閱讀 168,814評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵辣往,是天一觀的道長(zhǎng)兔院。 經(jīng)常有香客問(wèn)我,道長(zhǎng)站削,這世上最難降的妖魔是什么坊萝? 我笑而不...
    開封第一講書人閱讀 59,869評(píng)論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮钻哩,結(jié)果婚禮上屹堰,老公的妹妹穿的比我還像新娘。我一直安慰自己街氢,他們只是感情好扯键,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著珊肃,像睡著了一般荣刑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上伦乔,一...
    開封第一講書人閱讀 52,475評(píng)論 1 312
  • 那天厉亏,我揣著相機(jī)與錄音,去河邊找鬼烈和。 笑死爱只,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的招刹。 我是一名探鬼主播恬试,決...
    沈念sama閱讀 41,010評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼疯暑!你這毒婦竟也來(lái)了训柴?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,924評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤妇拯,失蹤者是張志新(化名)和其女友劉穎幻馁,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體越锈,經(jīng)...
    沈念sama閱讀 46,469評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡仗嗦,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了甘凭。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片稀拐。...
    茶點(diǎn)故事閱讀 40,680評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖对蒲,靈堂內(nèi)的尸體忽然破棺而出钩蚊,到底是詐尸還是另有隱情,我是刑警寧澤蹈矮,帶...
    沈念sama閱讀 36,362評(píng)論 5 351
  • 正文 年R本政府宣布砰逻,位于F島的核電站,受9級(jí)特大地震影響泛鸟,放射性物質(zhì)發(fā)生泄漏蝠咆。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評(píng)論 3 335
  • 文/蒙蒙 一北滥、第九天 我趴在偏房一處隱蔽的房頂上張望刚操。 院中可真熱鬧,春花似錦再芋、人聲如沸菊霜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)鉴逞。三九已至,卻和暖如春司训,著一層夾襖步出監(jiān)牢的瞬間构捡,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工壳猜, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留勾徽,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,099評(píng)論 3 378
  • 正文 我出身青樓统扳,卻偏偏與公主長(zhǎng)得像喘帚,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子闪幽,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容