1. 程式人生 > >自適應閾值大津法(OTSU)介紹及程式碼實現

自適應閾值大津法(OTSU)介紹及程式碼實現

https://blog.csdn.net/a153375250/article/details/50970104

演算法原理
最大類間方差法是由日本學者大津於1979年提出的,是一種自適應的閾值確定法,又叫大津法,簡稱OTSU。

我用最簡單的方式解釋一下演算法原理:

這個演算法的思想就是假設閾值T將影象分成了前景和背景兩個部分。

求出這兩個部分的類間方差:

前景畫素個數佔比x(前景平均灰度 - 全圖平均灰度)2 + 背景畫素個數佔比x(背景平均灰度 - 全圖平均灰度)2

將閾值從0~255遍歷一次,使上述類間方差最大的閾值T即為所求。

類間方差計算
我們接下來將演算法原理中的類間方差化簡為一個比較簡單的形式,下面的過程也是程式碼實現的過程。

定義變數:

總畫素數:N
前景畫素數:fN
背景畫素數:bN
前景畫素灰度和:fSum
背景畫素灰度和:bSum
前景畫素平均灰度:fu
背景畫素平均灰度:bu
影象總灰度值:Sum
影象平均灰度:u
前景畫素佔比:fw
背景影象佔比:bw
類間方差:g
1
2
3
4
5
6
7
8
9
10
11
12
前景、背景佔比:

前景畫素佔比:fw = fN/N
背景畫素佔比:bw = bN/N
1
2
前景、背景佔比滿足:

fw + bw =1  (1)
1
圖片平均灰度值:

圖片灰度直方圖 : Histogram[256]
圖片總灰度值 Sum : for(i=0; i<N; i++){ Sum += Histogram[i];}
圖片平均灰度值 u : Sum/N
1
2
3
閾值為T時前景平均灰度:

閾值為T時前景畫素數 fN : for(i=0; i<=T; i ++) { fN += Histogram[i];}
閾值為T時前景畫素灰度和 fSum : for(i=0; i<=T; i++) { fSum += Histogram[i]*i;}
閾值為T時前景平均灰度 fu : fSum/fN
1
2
3
閾值為T時背景平均灰度:

閾值為T時背景畫素數 bN : N-fN
閾值為T時背景畫素灰度和 bSum : Sum-fSum
閾值為T時背景平均灰度 bu : bSum/bN    
1
2
3
平均灰度滿足:

u = fu*fw + bu*bw  (2)
1
類間方差:

類間方差 g:fw*(fu-u)^2+bw*(bu-u)^2  (3)
1
將(1)(2)帶入(3)式:

g = bw*fw*(fu-bu)^2
1
程式碼實現
假設讀入圖片為單通道Mat型灰度圖,我們可以用以下程式碼實現:

#include <opencv2/opencv.hpp>  
#include <cv.h>
#include <highgui.h>
#include <cxcore.h>

using namespace std;
using namespace cv;

Mat otsuGray(const Mat src) {
    Mat img = src;
    int c = img.cols; //影象列數
    int r = img.rows; //影象行數
    int T = 0; //閾值
    uchar* data = img.data; //資料指標
    int ftNum = 0; //前景畫素個數
    int bgNum = 0; //背景畫素個數
    int N = c*r; //總畫素個數
    int ftSum = 0; //前景總灰度值
    int bgSum = 0; //背景總灰度值
    int graySum = 0;
    double w0 = 0; //前景畫素個數佔比
    double w1 = 0; //背景畫素個數佔比
    double u0 = 0; //前景平均灰度
    double u1 = 0; //背景平均灰度
    double Histogram[256] = {0}; //灰度直方圖
    double temp = 0; //臨時類間方差
    double g = 0; //類間方差

    //灰度直方圖
    for(int i = 0; i < r ; i ++) {
        for(int j = 0; j <c; j ++) {
            Histogram[img.at<uchar>(i,j)]++;
        }
    }
    //求總灰度值
    for(int i = 0; i < 256; i ++) {
        graySum += Histogram[i]*i;
    }

    for(int i = 0; i < 256; i ++) {
        ftNum += Histogram[i];  //閾值為i時前景個數
        bgNum = N - ftNum;      //閾值為i時背景個數
        w0 = (double)ftNum/N; //前景畫素佔總數比
        w1 = (double)bgNum/N; //背景畫素佔總數比
        if(ftNum == 0) continue;
        if(bgNum == 0) break;
        //前景平均灰度
        ftSum += i*Histogram[i];
        u0 = ftSum/ftNum;

        //背景平均灰度
        bgSum = graySum - ftSum;
        u1 = bgSum/bgNum;

        g = w0*w1*(u0-u1)*(u0-u1);
        if(g > temp) {
            temp = g;
            T = i;
        }
    }

    for(int i=0; i<img.rows; i++)
    {
        for(int j=0; j<img.cols; j++)
        {
            if((int)img.at<uchar>(i,j)>T)
                img.at<uchar>(i,j) = 255;
            else
                img.at<uchar>(i,j) = 0;
        }
    }
    return img;
}
--------------------- 
作者:Easy-Sir 
來源:CSDN 
原文:https://blog.csdn.net/a153375250/article/details/50970104 
版權宣告:本文為博主原創文章,轉載請附上博文連結!