opencv-閾值分割


 

關於自適應閾值,可參考:Wellner 自適應閾值二值化算法

 

一、大津法OTSU(最大類間方差法)

 

參考:非黑即白——圖像分割入門篇之Otsu閾值

自適應閾值分割—大津法(OTSU算法)C++實現

灰度圖像的自動閾值分割(Otsu 法)

 

在實際運用過程中,大津法表現得最穩定,且無需參數,對於現實圖像保持了最好的均勻性和形狀特性,而且被商業軟件GIMP 和學術軟件Matlab采納為自動閾值法。

 

原理:

Otsu分割方法求取閾值是求得使類間方差最大的閾值:

假設待分割圖像的像素數為N(就是常說的幾百萬像素了),它有L個灰度級(0,1,…,L-1),灰度級為i的像素數為ni,那么直方圖概率密度pi=ni/N(通俗說法就是這一部分像素所占的比例)。假設閾值t將圖像分成兩類C0和C1,則C0和C1分別對應具有灰度級{0,1,…,k}和{k+1,k+2,…,L-1}的像素。設σB(k)表示閾值為k時的類間方差,則最佳閾值T可以通過求其最大值而得到,

這是什么意思呢?σB(k)是隨着k而變化的,那么在k的變化過程中,σB(k)總有一個最大值,T就等於那個對應的k。

 

上面一直憋着類間方差沒有說,它的定義是這樣的:

 

其中p1(k),p2(k)分別是兩類的概率密度,m1(k),m2(k)分別是C0和C1的均值(C0和C1都對應一堆像素值,直接求數學平均值),mg是圖像的全局均值。

 

Otsu方法加權地使用了兩類的灰度均值信息和概率密度信息,考慮了兩類的分布,在實際的應用中取得了很好的效果,彩色分割都可以哦,不過是經過了灰度變換了,不僅僅是簡單的灰度變換,下面分割結果通過疊加輪廓展示。

它尤其適用於前背景的方差相差不大的情況。

 

Otsu實現思路

1. 計算0~255各灰階對應的像素個數,保存至一個數組中,該數組下標是灰度值,保存內容是當前灰度值對應像素數;

2. 計算背景圖像的平均灰度、背景圖像像素數所占比例;

3. 計算前景圖像的平均灰度、前景圖像像素數所占比例;

4. 遍歷0~255各灰階,計算並尋找類間方差極大值;

 

 

使用時可直接調用opencv中的threshold函數,將第五個參數設置為CV_THRESH_OTSU

C++代碼實現:

int OtsuAlgThreshold(const Mat image) { if(image.channels()!=1) { cout<<"Please input Gray-image!"<<endl; return 0; } int T=0; //Otsu算法閾值; 
    double varValue=0; //類間方差中間值保存 ;
    double w0=0; //前景像素點數所占比例 ;
    double w1=0; //背景像素點數所占比例 ;
    double u0=0; //前景平均灰度 ;
    double u1=0; //背景平均灰度 ;
    double Histogram[256]={0}; //灰度直方圖,下標是灰度值,保存內容是灰度值對應的像素點總數 ;
    uchar *data=image.data; double totalNum=image.rows*image.cols; //像素總數 ; //計算灰度直方圖分布,Histogram數組下標是灰度值,保存內容是灰度值對應像素點數 ;

    for(int i=0;i<image.rows;i++)   //為表述清晰,並沒有把rows和cols單獨提出來 ;
 { for(int j=0;j<image.cols;j++) { Histogram[data[i*image.step+j]]++; } } for(int i=0;i<255;i++) { //每次遍歷之前初始化各變量 ; 
        w1=0;       u1=0;       w0=0;       u0=0; //***********背景各分量值計算************************** 
        for(int j=0;j<=i;j++) //背景部分各值計算;
 { w1+=Histogram[j];  //背景部分像素點總數 ; 
            u1+=j*Histogram[j]; //背景部分像素總灰度和 ;
 } if(w1==0) //背景部分像素點數為0時退出 ;
 { break; } u1=u1/w1; //背景像素平均灰度; 
        w1=w1/totalNum; // 背景部分像素點數所占比例; //***********背景各分量值計算************************** //***********前景各分量值計算************************** 
        for(int k=i+1;k<255;k++) { w0+=Histogram[k];  //前景部分像素點總數 ; 
            u0+=k*Histogram[k]; //前景部分像素總灰度和 ;
 } if(w0==0) //前景部分像素點數為0時退出 ;
 { break; } u0=u0/w0; //前景像素平均灰度 ; 
        w0=w0/totalNum; // 前景部分像素點數所占比例 ; //***********前景各分量值計算************************** //***********類間方差計算****************************** 
        double varValueI=w0*w1*(u1-u0)*(u1-u0); //當前類間方差計算 ;

        if(varValue<varValueI) { varValue=varValueI; T=i; } } return T; } 

 

大津算法可以從圖像直方圖上有一個更為直觀的理解:大津閾值大致上是直方圖兩個峰值之間低谷的值。

Otsu 方法也不是萬能的。當目標與背景的大小比例懸殊時,類間方差准則函數可能呈現雙峰或多峰,此時效果不好。這時就要考慮其他的辦法了。

 

二、最大熵閾值分割

 

參考:最大熵閾值分割法

OpenCV學習筆記(二)之最大熵閾值分割

 

 

原理:

 

1.頻率和概率

 

    直方圖每個矩形框的數值描述的是圖像中相應灰度值的頻率。因此,可以說直方圖是一種離散的頻率分布。給定一個大小為M*N的圖像I,直方圖中所有矩形框所代表的數值之和,即為圖像中的像素數量,即:

 

 

    相對應的歸一化直方圖表示為:

 

 

   0<=i<K 通常被解釋為一個隨機過程的概率分布或概率密度函數,表示的是圖像中像素灰度值為i所出現的概率。i的累積概率值為1,即概率分布p必須滿足以下關系:

 

 

    與累積概率所所對應的累積直方圖H是一個離散的分布函數P(),(通常也稱為累積分布函數或cdf):

 

 

2.最大熵閾值分割

    熵是信息理論中一個重要的概念,這種方法常用於數據壓縮領域。熵是一種統計測量方法,用以確定隨機數據源中所包含的信息數量。例如,包含有N個像素的圖像I,可以解釋為包含有N個符號的信息,每一個符號的值都獨立獲取於有限范圍K(e.g.,256)中的不同灰度值。

   將數字圖像建模為一種隨機信號處理,意味着必須知道圖像灰度中每一個灰度g所發生的概率,即:

 

  因為所有概率應該事先知道,所以這些概率也稱為先驗概率。對於K個不同灰度值g=0,…,K-1的概率向量可以表示為:

 上述概率向量也稱為概率分布或稱為概率密度函數(pdf)。實際數字圖像處理應用當中,先驗的概率通常是不知道的,但是這些概率可以通過在一幅圖像或多幅圖像中觀察對應灰度值所發生的頻率,從而估算出其概率。圖像概率密度函數p(g)可以通過歸一化其對應的直方圖獲得其概率,即:

 

3.最大熵

    數字圖像中給定一個估算的概率密度函數p(g),數字圖像中的熵定義為:

4.用圖像熵進行圖像分割

    利用圖像熵為准則進行圖像分割有一定歷史了,學者們提出了許多以圖像熵為基礎進行圖像分割的方法。我們介紹一種由Kapuret al提出來,現在仍然使用較廣的一種圖像熵分割方法。

給定一個特定的閾值q(0<=q<K-1),對於該閾值所分割的兩個圖像區域C0,C1,其估算的概率密度函數可表示為:

 

 同樣的,背景熵可以改寫為:


代碼:

//一維最大熵閾值分割;
float caculateCurrentEntropy(Mat hist, int threshold) { float backgroundSum=0,targetSum=0; float * pDataHist=hist.ptr<float>(0); for (int i=0;i<256;i++) { //累積背景值;
        if (pDataHist[i]<threshold) { backgroundSum+=pDataHist[i]; } //累積目標值;
        else { targetSum+=pDataHist[i]; } } float BackgroundEntropy = 0, targetEntropy = 0; for (int i=0;i<256;i++) { //計算背景熵;
        if (i < threshold) { if (pDataHist[i] == 0) continue; float ratio1 = pDataHist[i] / backgroundSum; //計算當前能量熵;
            BackgroundEntropy += -ratio1*logf(ratio1); } else  //計算目標熵;
 { if (pDataHist[i] == 0) continue; float ratio2 = pDataHist[i] / targetSum; targetEntropy += -ratio2*logf(ratio2); } } return (BackgroundEntropy+targetEntropy); } void tseg(Mat img,Mat &res) {//計算原圖一維灰度直方圖;
    int nimages=1; int channels[1]={0}; Mat outputHist; int dims=1; int histSize[1]={256}; float hranges[2]={0,255}; const float *ranges[1]={hranges}; calcHist(&img,nimages,channels,Mat(),outputHist,dims,histSize,ranges); float maxentropy = 0; int max_index = 0; for (int i = 0; i < 256; i++) { float cur_entropy = caculateCurrentEntropy(outputHist, i); if (cur_entropy > maxentropy) { maxentropy = cur_entropy; max_index = i; } } threshold(img, res, max_index, 255, CV_THRESH_BINARY); }

 

三、區域生長法

 

參考:OpenCV - 區域生長算法

 

 

 

其它參考:

七種常見閾值分割代碼(Otsu、最大熵、迭代法、自適應閥值、手動、迭代法、基本全局閾值法)

OpenCV—使用積分圖像統計像素     利用積分圖像可以進行自適應閾值分割。

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM