Matlab 小工具(3)----影象二值化的閾值計算
影象分割是影象處理這門學科中的基礎操作,基於閾值的分割則又是影象分割的最基本的難題之一,其難點在於閾值的選取。事實證明,閾值的選擇的恰當與否對分割的效果起著決定性的作用。由於閾值選取對影象分割的基礎性,本文主要在【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;