9. OpenCV--圖像二值化(Binary Image)

》》點贊,收藏+關註,理財&技術不迷路《《

目錄:

9.1 二值化概念與原理

二值化 —— 圖像隻有兩種色,黑白。0-1,0=黑,1=白

灰度:0-255

彩色:255,255,255

定義:圖像的二值化,就是將圖像上的像素點的灰度值設置為0或255,也就是將整個圖像呈現出明顯的隻有黑和白的視覺效果。

一幅圖像包括目標物體、背景還有噪聲,要想從多值的數字圖像中直接提取出目標物體,常用的方法就是設定一個閾值T,用T將圖像的數據分成兩部分:大於T的像素群和小於T的像素群。這是研究灰度變換的最特殊的方法,稱為圖像的二值化(Binarization)。

其實圖像二值化的方法有不下二十種。一般分為Global和Local兩類,區別就是尋找那個閾值的時候使用到瞭哪裡的信息。

一般是為瞭將感興趣目標和背景分離。

圖像的二值化是最簡單的圖像處理技術,它一般都跟具體算法聯系在一起,很多算法的輸入需要是二值數據。比如你把圖像文字轉換為PDF文字,PDF上隻能是黑白兩種顏色。比如你給二維碼解碼,你需要知道哪塊黑哪塊白。

去掉圖像兩個字,二值化在視覺裡面應用場景多一些。比如早期的人臉檢測很多手勢識別的方法,第一步要找到皮膚塊,所以需要把圖像分為皮膚區域和非皮膚區域,這也算是一種二值化,但通常在方法上用的可能不是基於直方圖的瞭(可能是ID3, 隨機森林,SVM,甚至神經網絡)。比如Haar特征,以及後來的BRIEF和FAST等,還有LBP實際上都是用到二值的思想。

圖像二值化可以看作是聚類,可以看作是分類……這些其實不重要,重要的是它快。它最明顯的意義就是簡化後期的處理,提高處理的速度。`

但是單一的圖像二值化方法(指基於直方圖的二值化)往往比不過其他的方法,因為,畢竟你信息丟瞭太多。但是二值化快啊……你可以進行一百次不同的二值化,然後再得到一個更好的結果……

上面右圖中下面兩個圖是還可以對二值圖像進行編碼。編碼後就更好壓縮瞭,那麼我們就可以高度節省內存。

圖像二值化方法:

全局閾值

局部閾值

9.2 OpenCV中圖像二值化方法:

評判某個算法是否好,就看二值化圖像信息是否丟失瞭很多。

threshold(gray_src, dst, threshold_value, threshold_max,THRESH_BINARY);

//原圖,目標圖,已知閾值,閾值最大值,閾值類型

返回閾值的值和圖像。

閾值:簡單點說是把圖像分割的標尺。這個標尺是根據什麼產生的,可以用閾值產生算法(opencv有兩個算法)或者是自己指定一個閾值來進行分割。

如下分蘋果,大於某一像素的變為黑色,小於某一像素的變為白色,即可通過閾值把圖像分割瞭。

可以根據有5種閾值類型來分割(Binary segmentation二值分割)。

printf("%d", THRESH_BINARY); //0,二值化

printf("%d", THRESH_BINARY_INV); //1,反二值化

printf("%d", THRESH_TRUNC); //2,截斷

printf("%d", THRESH_TOZERO); //3,取零

printf("%dn", THRESH_TOZERO_INV); //4,反取零

9.2.1 5種閾值處理類型(手動)

9.2.1.1 閾值二值化(threshold binary)

THRESH_BINARY

threshold(gray_src, dst, threshold_value, threshold_max, THRESH_BINARY);

//閾值二值化

左下方直方圖表示圖像像素點Src(x,y)值的分佈情況,藍色水平線表示閾值,大於閾值的取最大值255,小於閾值的取最小值0:

9.2.1.2 閾值反二值化(threshold binary Inverted)

THRESH_BINARY_INV

左下方的圖表示圖像像素點Src(x,y)值分佈情況,藍色水平線表示閾值,大於閾值的取最小值0,小於閾值的取最大值255:

9.2.1.3 截斷 (truncate)

THRESH_TRUNC

左下方的圖表示圖像像素點Src(x,y)值分佈情況,藍色水平線表示閾值 ,大於閾值的跟閾值相等,小於閾值的不變:

9.2.1.4 閾值取零 (threshold to zero)

THRESH_TOZERO

左下方的圖表示圖像像素點Src(x,y)值分佈情況,藍色水平線表示閾值,大於閾值的不變,小於閾值的取最小值0:

9.2.1.5 閾值反取零 (threshold to zero inverted)

THRESH_TOZERO_INV

左下方的圖表示圖像像素點Src(x,y)值分佈情況,藍色水平線表示閾值,大於閾值的取最小值0,小於閾值的不變:

9.2.2 2種閾值尋找方法(自動/全局; 基於直方圖)

threshold(gray_src, dst, 0, 255, THRESH_OTSU | type_value);

//自動計算二值化otsu閾值,忽略輸入的閾值

9.2.2.1 Triangle三角閾值法(基於直方圖)

該方法是使用直方圖數據,基於純幾何方法來尋找最佳閾值,它的成立條件是假設直方圖最大波峰在靠近最亮的一側,然後通過三角形求得最大直線距離,根據最大直線距離對應的直方圖灰度等級即為分割閾值,圖示如下:

在直方圖上從最高峰處bmx到最暗對應直方圖bmin(p=0)%構造一條直線,從bmin處開始計算每個對應的直方圖b到直線的垂直距離,知道bmax為止,其中最大距離對應的直方圖位置即為圖像二值化對應的閾值T。

有時候最大波峰對應位置不在直方圖最亮一側,而在暗的一側,這樣就需要翻轉直方圖,翻轉之後求得值,用255減去即得到為閾值T。擴展情況的直方圖表示如下

所以triangle 非常適合用在隻有單個波峰時候!!!!多個波峰的話,應用起來會很差。所以做醫學圖像處理會很好,因為細胞圖片的直方圖最多都隻有三個波峰,這個方法是從生物醫學衍生出的。

算法步驟

1. 圖像轉灰度

2. 計算圖像灰度直方圖

3. 尋找直方圖中兩側邊界

4. 尋找直方圖最大值

5. 檢測是否最大波峰在亮的一側,否則翻轉

6. 計算閾值得到閾值T,如果翻轉則255-T

左邊為原圖,通過直方圖來看像素值分佈,通過閾值保留,我們看右圖發現前景被保留下來瞭所以為白色,背景就為黑色瞭。好的二值化方法就應該如右圖一樣。

二值化方法中的三角二值化:通過直方圖來對圖像進行三角二值化,連接波峰波谷然後做垂直線。

9.2.2.2 OTSU閾值法(基於直方圖)

直方圖統計學

otsu 大津算法介紹:

OTSU算法是由日本學者OTSU於1979年提出的一種對圖像進行二值化的高效算法。

利用閾值將原圖像分成前景,背景兩個圖象。

前景:用n1,csum,m1來表示在當前閾值下的前景的點數,質量矩,平均灰度

背景:用n2, sum-csum,m2來表示在當前閾值下的背景的點數,質量矩,平均灰度

當取最佳閾值時,背景應該與前景差別最大,關鍵在於如何選擇衡量差別的標準,而在otsu算法中這個衡量差別的標準就是最大類間方差,在本程序中類間方差用sb表示,最大類間方差用fmax

這段引用自百度百科,不是很好懂。

otsu 大津算法原理

otsu 大津算法是一種圖像二值化算法,作用是確定將圖像分成黑白兩個部分的閾值。

將圖像背景和前景分成黑白兩類很好理解,但是如何確定背景和前景的二值化界限(閾值)呢?

對於不同的圖像,這個閾值可能不同,這就需要有一種算法來根據圖像的信息自適應地確定這個閾值。

首先,需要將圖像轉換成灰度圖像,255個灰度等級。

可以將圖像理解成255個圖層,每一層分佈瞭不同的像素,這些像素垂直疊加合成瞭一張完整的灰度圖。

我們的目的就是找到一個合適的灰度值,大於這個值的我們將它稱之為背景(灰度值越大像素越黑),小於這個值的我們將它稱之為前景(灰度值越小像素越白)。

怎麼確定這個值就是我們想要的值呢?

這裡引入方差的概念,方差越大,相關性越低,黑白越分明。

我們將每一個灰度值之上下之間的像素的方差求出來不就行瞭嗎?找到方差最大的那個灰度值,那個就是我們想要的二值化分隔閾值。

先定義幾個符號代表的意義:

h:圖像的寬度

w:圖像的高度(h*w 得到圖像的像素數量)

t :灰度閾值(我們要求的值,大於這個值的像素我們將它的灰度設置為255,小於的設置為0)

n0:小於閾值的像素,前景

n1:大於等於閾值的像素,背景

n0 + n1 == h * w

w0:前景像素數量占總像素數量的比例

w0 = n0 / (h * w)

w1:背景像素數量占總像素數量的比例

w1 = n1 / (h * w)

w0 + w1 == 1

u0:前景平均灰度

u0 = n0灰度累加和 / n0

u1:背景平均灰度

u1 = n1灰度累加和 / n1

u:平均灰度

u = (n0灰度累加和 + n1灰度累加和) / (h * w) 根據上面的關系

u = w0 * u0 + w1 * u1

g:類間方差(那個灰度的g最大,哪個灰度就是需要的閾值t)

g = w0 * (u0 – u)^2 + w1 * (u1 – u)^2

根據上面的關系,可以推出:(這個一步一步推導就可以得到)

g = w0 * w1 * (u0 – u1) ^ 2

然後,遍歷每一個灰度值,找到這個灰度值對應的 g

找到最大的 g 對應的 t

9.2.3 自適應閾值(局部閾值)

可以想象為把矩陣分成模塊化,然後各個模塊求平均和閾值比較。(所以又有兩種平均方法,還有模塊大小選擇,比較方法是直接單獨比較?還是相差在某個范圍內?後者好,因為可以考慮到誤差,誤差 = 像素值中混雜瞭噪音)

這個方法隻返回binary圖像,不返回set閾值的值。

C++: void adaptiveThreshold(InputArray src, OutputArray dst, double maxValue, int adaptiveMethod, int thresholdType, int blockSize, double C)

InputArray src:源圖像

OutputArray dst:輸出圖像,與源圖像大小一致

int adaptiveMethod:在一個鄰域內計算閾值所采用的算法,有兩個取值,分別為ADAPTIVE_THRESH_MEAN_C 和 ADAPTIVE_THRESH_GAUSSIAN_C 。

ADAPTIVE_THRESH_MEAN_C的計算方法是計算出領域的平均值再減去第七個參數double C的值

ADAPTIVE_THRESH_GAUSSIAN_C的計算方法是計算出領域的高斯均值再減去第七個參數double C的值

int thresholdType:這是閾值類型,隻有兩個取值,分別為 THRESH_BINARY 和THRESH_BINARY_INV 具體的請看官方的說明,這裡不多做解釋

int blockSize:adaptiveThreshold的計算單位是像素的鄰域塊,鄰域塊取多大,就由這個值作決定

double C:在對參數int adaptiveMethod的說明中,我已經說瞭這個參數的作用,從中可以看出,這個參數實際上是一個偏移值調整量

從上面的說明中可以看出,使用函數adaptiveThreshold的關鍵是確定blockSize和C的值,明白瞭這兩個值的意義之後,在實際項目中,應該可以根據試驗法選出較為合適的值吧!

我們改為高斯閾值:

9.2.4 自定義分割

這裡我們定義全局平均數來作為閾值分割圖像。

上面這一大步是為瞭將數組矩陣拉直然後求出平均數。一定要先轉為 灰度圖,因為rgb有三個通道。

9.3 超大圖像二值化

# 將大圖片拆分成小圖片後再用自適應局部閾值比較好

# 因為超大圖像一般都是幾千萬或者幾億像素。1000*1000像素也才100萬像素。

# 對於超大像素而言,一個窗口根本顯示不瞭那麼多像素。要麼就壓縮才能顯示。

9.3.1 全局閾值(無法排除噪聲影響)

本來應該是白色的地方,處理後灰度圖中出現瞭雪花格子,這是噪聲引起的,因為我們的OTSU有這個缺點。所以全局閾值有缺點。

9.3.2 全局閾值優化(手動過濾)

全局閾值的上述問題可以通過下面這個方法解決,也可以通過局部閾值解決。以下方法就是把方差小於 某個數 然後直接覆蓋為255或0. 這樣就排除瞭噪聲幹擾(當然也有可能影響到一點點原圖像)但肯定比原來要好很多。

9.3.3 局部閾值(可以解決噪聲問題)

局部閾值方法中我們考慮瞭噪聲的影響,下面方法我們設立的噪聲范圍在+-20內,但是全局閾值就沒有考慮噪聲,所以直接二值化會出現很多錯誤。

我們還輸出瞭均值和方差,方差為0,均值都為255,說明這一塊是空白圖像。我們也可以當方差或者mean小於某個數時,直接把它給賦值為0. 這樣就可以消除一些噪聲產生的誤差。

赞(0)