大津算法

使用大津算法二值化的圖像
原圖
在計算機視覺和圖像處理領域，大津二值化法（Otsu's method）是由Nobuyuki Otsu命名的一種用於自動圖像閾值分割的方法。 該方法用於對基於聚類的圖像進行二值化，即將一個灰度圖像轉化為二值圖像。算法假定圖像根據雙模直方圖（前景像素和背景像素）包含兩類像素，並計算出能將這兩類像素分開的最佳閾值，使得類內方差最小，或等效地，類間方差最大。
因此，大津二值化法是一種一維離散的費舍爾判別分析的類似方法，與Jenks natural breaks optimization相關，並且等同於在強度直方圖上執行的全局最優k-means算法。
原始論文中還描述了多級閾值擴展方法， 並且後來提出了計算效率高的實現方法。

**方法**
在大津算法中，我們窮舉搜索能使類內方差最小的閾值，定義為兩個類的方差的加權和：

權重  是被閾值  分開的兩個類的概率，而  是這兩個類的方差。
大津證明了最小化類內方差和最大化類間方差是相同的：

用類概率  和類均值  來表示。
類概率  用閾值為  的直方圖計算：

而類均值  為：

其中  為第  個直方圖面元中心的值。
同樣的，你可以對大於  的面元求出右側直方圖的  與 。
類概率和類均值可以迭代計算。這個想法會產生一個有效的算法。
大津算法得出了0:1範圍上的一個閾值。這個閾值用於圖像中出現的像素強度的動態範圍。例如，若圖像只包含155到255之間的像素強度，大津閾值0.75會映射到灰度閾值230（而不是192，因為圖像包含的像素不是0–255全範圍的）。常見的攝影圖像往往包含全範圍的像素強度，就會讓這一點有爭論，但其他應用會對這點區別很敏感。

**算法**
# 計算每個強度級的直方圖和概率
# 設置  和  的初始值
# 遍歷所有可能的閾值  最大強度
## 更新  和 
## 計算 
# 所需的閾值對應於最大的 
# 你可以計算兩個最大值（和兩個對應的）。 是最大值而  是更大的或相等的最大值
# 所需的閾值 = 

**JavaScript實現**
注：輸入參數total是給定圖像中的像素數。輸入參數histogram是灰度圖像不同灰度級（典型的8位圖像）的256元直方圖。此函數輸出圖像的閾值。
function otsu(histogram, total) {
var sum = 0;
for (var i = 1; i = max ) {
threshold1 = i;
if ( between > max ) {
threshold2 = i;
}
max = between;            
}
}
return ( threshold1 + threshold2 ) / 2.0;
}

**C實現**
unsigned char otsu_threshold( int* histogram, int pixel_total )
{
unsigned int sumB = 0;
unsigned int sum1 = 0;
float wB = 0.0f;
float wF = 0.0f;
float mF = 0.0f;
float max_var = 0.0f;
float inter_var = 0.0f;
unsigned char threshold = 0;
unsigned short index_histo = 0;
for ( index_histo = 1; index_histo = max_var )
{
threshold = index_histo;
max_var = inter_var;
}
}
return threshold;
}

**MATLAB實現**
total為給定圖像中的像素數。
histogramCounts為灰度圖像不同灰度級（典型的8位圖像）的256元直方圖。
level為圖像的閾值（double）。
function level = otsu(histogramCounts, total)
%% OTSU automatic thresholding method
sumB = 0;
wB = 0;
maximum = 0.0;
threshold1 = 0.0;
threshold2 = 0.0;
sum1 = sum((1:256).*histogramCounts.'); % the above code is replace with this single line
for ii=1:256
wB = wB + histogramCounts(ii);
if (wB == 0)
continue;
end
wF = total - wB;
if (wF == 0)
break;
end
sumB = sumB +  ii * histogramCounts(ii);
mB = sumB / wB;
mF = (sum1 - sumB) / wF;
between = wB * wF * (mB - mF) * (mB - mF);
if ( between >= maximum )
threshold1 = ii;
if ( between > maximum )
threshold2 = ii;
end
maximum = between;
end
end
level = (threshold1 + threshold2 )/(2);
end
另一種方法是用向量化方法（可以很容易地轉換為便於GPU處理Python矩陣數組版本）
function  threshold_otsu = Thredsholding_Otsu( Image)
%Intuition:
%(1)pixels are divided into two groups
%(2)pixels within each group are very similar to each other 
%   Parameters:
%   t : threshold 
%   r : pixel value ranging from 1 to 255
%   q_L, q_H : the number of lower and higher group respectively
%   sigma : group variance
%   miu : group mean
%   Author: Lei Wang
%   Date  : 22/09/2013
%   References : Wikepedia, 
%   for multi children Otsu method, please visit : https://drive.google.com/file/d/0BxbR2jt9XyxteF9fZ0NDQ0dKQkU/view?usp=sharing
%   This is my original work
nbins = 256;
counts = imhist(Image,nbins);
p = counts / sum(counts);
for t = 1 : nbins
q_L = sum(p(1 : t));
q_H = sum(p(t + 1 : end));
miu_L = sum(p(1 : t) .* (1 : t)') / q_L;
miu_H = sum(p(t + 1 : end) .* (t + 1 : nbins)') / q_H;
sigma_b(t) = q_L * q_H * (miu_L - miu_H)^2;
end
~,threshold_otsu = max(sigma_b(:));
end
該實現有一點點冗餘的計算。但由於大津算法快速，這個實現版本是可以接受，並且易於理解的。因為在一些環境中，如果使用向量化的形式，可以更快地運算循環。大津二值化法使用架構最小的堆－孩子標籤（heap-children label）可以很容易地轉變成多線程的方法。

**參考文獻**

**外部鏈接**
  Lecture notes on thresholdingdate=20160217112553  – covers the Otsu method.-  A plugin for ImageJdate=20151104032023  using Otsu's method to do the threshold.-  A full explanation of Otsu's methoddate=20151018141624  with a working example and Java implementation.-  Implementation of Otsu's methoddate=20151024172318  in ITK-  Otsu Thresholding in C#date=20120104114807  A straightforward C# implementation with explanation.-  Otsu's method using MATLABdate=20151026191848