您當前的位置:首頁 > 攝影

Matlab 小工具(3)----影象二值化的閾值計算

作者:由 若谷 發表于 攝影時間:2020-05-19

影象分割是影象處理這門學科中的基礎操作,基於閾值的分割則又是影象分割的最基本的難題之一,其難點在於閾值的選取。事實證明,閾值的選擇的恰當與否對分割的效果起著決定性的作用。由於閾值選取對影象分割的基礎性,本文主要在【1】、【2】、【3】、【4】等的基礎上,對一些當前流行的閾值選取演算法做了探討、實現和比較。多閾值分割雖然能進一步提高影象分割的質量,但由於它只是分割技巧的處理問題,而與單閾值分割並無本質的區別。因此本文並不對多閾值分割進行討論,而只考慮單閾值分割的情形。

本文的演算法,就是為了自動生成二值化閾值。以便取得最理想的影象分析效果。

1.雙峰法

雙峰法的原理及其簡單:它認為影象由前景和背景組成,在灰度直方圖上,前後二景都形成高峰,在雙峰之間的最低谷處就是影象的閾值所在。根據這一原理,我們給出了它的實現,部分程式碼如下(Pascal語言描述,以下同):

%%功能:統計灰度圖的畫素特徵,計算閾值。使得透過此閾值得出的二值化操作的結果,能得出好的效果。

% 灰度影象的背景是黑色背景

% Example: Gray_Image_path = ‘。\InputImage\cons_4_2。jpg’;

% Threshold_Value = GetThreshold(Gray_Image)

% 思路:提取直方圖中圖片背景所對應的波峰;計算該波峰後漸趨平穩時的最小畫素值。

% Setps

% 灰度化;

% 直方圖;

% 檢測出所有的波峰;(就是背景中,呈現最多的畫素值,其對應畫素數量)

% 遍歷波峰序列,得到第一個滿足下列條件的波峰

% A 檢測相鄰亮度值的畫素數量大小,有變化,就產生一個有效的波峰、波谷;

% B 波峰的右側相鄰波谷處的畫素數量,比影象平均畫素數量小(即,低於平均值,才稱得上是“谷”),否則過濾掉這個波谷和其右側波峰。

% C 從左側波谷到波峰,再到右側波谷,累計所佔的畫素總和,比低於整個畫面畫素的30%

%

% Author: Ruogu7 (380545156@qq。com)

% Date: 05/19, 2020

//intPeak、intPeak2、intValley:峰值和直方圖值

//intIndx::相應的灰度值

intPeak,intIndx,intPeak2,intIndx2,intValley,intValleyIndx:integer;

//初始雙峰值

intPeak:=0;

intPeak2:=0;

//取得第一峰值

for intLoop:=0 to 255 do

if intPeak<=intGrayLevel[intLoop] then

begin

intPeak:=intGrayLevel[intLoop];

intIndx:=intLoop;

end;

//取得第二峰值

for intLoop:=0 to 255 do

Begin

if (intPeak2<=intGrayLevel[intLoop]) and (intLoop<>intIndx) then

begin

intPeak2:=intGrayLevel[intLoop];

intIndx2:=intLoop;

end

end;

//取得雙峰之間的谷值

intValley:=intSize;

if intIndx2

for intLoop:=intIndx2 to intIndx do

if intValley>intGrayLevel[intLoop] then

begin

intValley:=intGrayLevel[intLoop];

intValleyIndx:=intLoop;

end;

從分割的效果來看,當前後景的對比較為強烈時,分割效果較好;否則基本無效。

2.迭代法

迭代法是基於逼近的思想,其步驟如下:

1.求出圖象的最大灰度值和最小灰度值,分別記為ZMAX和ZMIN,令初始閾值T0=(ZMAX+ZMIN)/2;

2.根據閾值TK將圖象分割為前景和背景,分別求出兩者的平均灰度值ZO和ZB;

3.求出新閾值TK+1=(ZO+ZB)/2;

4.若TK=TK+1,則所得即為閾值;否則轉2,迭代計算。

以下給出迭代求閾值的部分實現:

//閾值初始為0

intThresholdVal:=0;

intThresholdVal2:=0;

//總灰度值

intTotalGrayLevel:=0;

for intLoop:=0 to 255 do

if intGrayLevel[intLoop]<>0 then

intTotalGrayLevel:=intTotalGrayLevel+intLoop*intGrayLevel[intLoop];

//求出初始最大灰度值

for intLoop:=0 to 255 do

if intGrayLevel[intLoop]>0 then

begin

intLGrayLevel:=intLoop;

intThresholdVal:=intLoop;

break;

end;

//求出初始最小灰度值和初始閾值

for intLoop:=255 downto 0 do

if intGrayLevel[intLoop]>0 then

begin

intRGrayLevel:=intLoop;

intThresholdVal:=(intThresholdVal+intLoop)div 2;

break;

end;

//迭代求解

while intThresholdVal<>intThresholdVal2 do

begin

intThresholdVal2:=intThresholdVal;

intCount:=0;

intLGrayLevel:=0;

for intLoop:=0 to intThresholdVal do

if intGrayLevel[intLoop]<>0 then

begin

intCount:=intCount+intGrayLevel[intLoop];

intLGrayLevel:=intLGrayLevel+intLoop*intGrayLevel[intLoop];

end;

intRGrayLevel:=intTotalGrayLevel-intLGrayLevel;

intLGrayLevel:=intLGrayLevel div intCount;

intRGrayLevel:=intRGrayLevel div (intSize-intCount);

intThresholdVal:=(intLGrayLevel+intRGrayLevel)div 2;

end;

標簽: intLoop  閾值  intGrayLevel  灰度  分割