# 2020/3/14 Thresholding (二值化又稱為灰度分劃)
* <font size = 3>**定義**</font>
<font>所謂二值化影像即是影像中<span style = 'background-color:yellow'>**只有「黑」與「白」的表現**</span>,將一個彩色影像轉為灰階影像後,針對影像中的每個像素點,設定一個灰階值標準,也稱作<span style = 'background-color:yellow'>**閥值(threshold)**</span>,凡是影像本身灰度值大於閥值(threshold)便令其為<span style = 'background-color:yellow'>**白點**</span>,灰度值低於閥值(threshold)的便令其為<span style = 'background-color:yellow'>**黑點**</span>,如此可得到一個二元的影像。</font>
* <font size = 3>**直方圖**</font>
圖一:
二值化前直方圖的分佈,有明顯的波峰波谷,位於波谷的閥值可以區分圖像中的兩個不同灰階區塊,這就是一個適合的閥值(threshold)設定。
圖二:
二值化後的直方圖分布,所有像素點灰階值依閥值(threshold)被轉換為0或255,也就是黑或白。

## Threshold的種類
### 固定閾值二值化(Threshold)
* <font size = 3>**定義**</font>
<font size = 2>該閾值T需要人為給定,取值範圍為0-255</font>
<pre><font size = 2>程式碼函式:
<b>cv2.threshold(src, thresh, maxval, type)</b>
引數含義:
src - 輸入原影象,需為灰度影象
thresh - 閾值大小 (即閾值T)
maxval - 最大值 (一般指定為255,也可指定為其他數值)
type - 閾值模式(0為常規型的,1-4均為變式)</font>
</pre>
<pre><font size = 2>type可以取0-4中的任一個值,對應的含義如下:
0 : THRESH_BINARY - 當前點值大於閾值時,取Maxval,否則設定為0
1 : THRESH_BINARY_INV - 當前點值大於閾值時,設定為0,否則設定為Maxval
2 : THRESH_TRUNC - 當前點值大於閾值時,設定為閾值,否則不改變
3 : THRESH_TOZERO - 當前點值大於閾值時,不改變,否則設定為0
4 : THRESH_TOZERO_INV - 當前點值大於閾值時,設定為0,否則不改變</font></pre>
官方程式碼:
```python = 35
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('1234.png',0)
# ret-True或False,有讀取到圖像或沒有
ret,thresh1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY)
ret,thresh2 = cv2.threshold(img,127,255,cv2.THRESH_BINARY_INV)
ret,thresh3 = cv2.threshold(img,127,255,cv2.THRESH_TRUNC)
ret,thresh4 = cv2.threshold(img,127,255,cv2.THRESH_TOZERO)
ret,thresh5 = cv2.threshold(img,127,255,cv2.THRESH_TOZERO_INV)
titles = ['Original Image','BINARY','BINARY_INV','TRUNC','TOZERO','TOZERO_INV']
images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]
plt.figure(figsize=(10, 5))
for i in range(6):
plt.subplot(2,3,i+1),plt.imshow(images[i],'gray')
plt.title(titles[i])
plt.xticks([]),plt.yticks([])
plt.show()
```
呈現結果

### 自適應閾值二值化(Adaptive Threshold)
* <font size = 3>**定義**</font>
<font size = 2>自適應閾值演算法主要是基於均值實現,根據計算均值的方法不同分為box-filter模糊均值與GAUSSIAN模糊均值</font>
<pre><font size = 2>程式碼函式:
<b>cv2.adaptiveThreshold(src, maxval, adaptiveMethod, type, Block-Size, C)</b>
引數含義:
src - 輸入原影象,需為灰度影象
maxval - 最大值 (一般指定為255,也可指定為其他數值)
adaptiveMethod - 計算均值方法(模式0-1)
type - 閾值模式(0為常規型的,1-4均為變式)
Block-Size - adaptiveMethod的計算單位是鄰域塊,鄰域塊取多大,由這個值做決定
(取值必須是奇數,影象較大,取127左右,對於小影象取25左右)
C - 常數,偏移值调整量
(閾值就等於平均值或者加權平均值減去這個常數)</font>
</pre>
<pre><font size = 2>thresh_type可以取0-1中值,對應的含義如下:
0 : cv2.ADAPTIVE_THRESH_MEAN_C - 計算方法是計算出領域的平均值再減去引數C
1 : cv2.ADAPTIVE_THRESH_GAUSSIAN_C - 計算方法是計算出領域的高斯均值再減去引數C
</font></pre>
<pre><font size = 2>type可以取0-4中的任一個值,對應的含義如下:
0 : THRESH_BINARY - 當前點值大於閾值時,取Maxval,否則設定為0
1 : THRESH_BINARY_INV - 當前點值大於閾值時,設定為0,否則設定為Maxval
2 : THRESH_TRUNC - 當前點值大於閾值時,設定為閾值,否則不改變
3 : THRESH_TOZERO - 當前點值大於閾值時,不改變,否則設定為0
4 : THRESH_TOZERO_INV - 當前點值大於閾值時,設定為0,否則不改變</font></pre>
官方程式碼:
```python=90
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('dave.jpg',0)
img = cv2.medianBlur(img,5)
ret,th1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY)
th2 = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_MEAN_C,\
cv2.THRESH_BINARY,11,2)
th3 = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,\
cv2.THRESH_BINARY,11,2)
titles = ['Original Image', 'Global Thresholding (v = 127)',
'Adaptive Mean Thresholding', 'Adaptive Gaussian Thresholding']
images = [img, th1, th2, th3]
plt.figure(figsize=(10, 10))
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()
```
呈現結果

### Otsu’s Binarization Algorithm
* <font size = 3>**定義**</font>
<font size = 2>它是按影象的灰度特性,將影象分成背景和目標兩部分。背景和目標之間的類間方差越大,說明構成影象的兩部分的差別越大,當部分目標錯分為背景或部分背景錯分為目標都會導致兩部分差別變小。因此,使類間方差最大的分割意味著錯分概率最小。</font>
* Otsu過程:
1. 計算圖像直方圖;
2. 設定一閾值,把直方圖強度大於閾值的像素分成一組,把小於閾值的像素分成另外一組;
3. 分別計算兩組內的偏移數,並把偏移數相加;
4. 把0~255依照順序多為閾值,重複1-3的步驟,直到得到最小偏移數,其所對應的值即為結果閾值。
官方程式碼:
```python=127
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('noisy2.png',0)
# global thresholding
ret1,th1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY)
# Otsu's thresholding
ret2,th2 = cv2.threshold(img,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
# Otsu's thresholding after Gaussian filtering
blur = cv2.GaussianBlur(img,(5,5),0)
ret3,th3 = cv2.threshold(blur,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
# plot all the images and their histograms
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','Histogram',"Otsu's Thresholding"]
plt.figure(figsize=(8, 10))
for i in range(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.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()
```
呈現結果

## 心得
Threshold在對於圖片的前置處理有很大的幫助,它可以將一些隱藏在圖像中或是不易發覺的資訊有效的顯現出來,大概就這樣,之後我有查到Threshold後的後續處理,來增強影像上的特徵或是紋理,如果之後要用到,就到這個[網站](https://smasoft-tech.com/tag/%E5%BD%B1%E5%83%8F%E5%89%8D%E8%99%95%E7%90%86%E3%80%81%E5%BD%B1%E5%83%8F%E9%A0%90%E8%99%95%E7%90%86%E3%80%81%E6%A9%9F%E5%99%A8%E8%A6%96%E8%A6%BA%E3%80%81%E4%BA%8C%E5%80%BC%E5%8C%96%E3%80%81%E5%BD%A2-2/)