圖像二值化——OTSU大津法

最大類間方差法是由日本學者大津(Nobuyuki Otsu)於1979年提出的,是一種自適應的閾值確定的方法,又叫大津法,簡稱OTSU。它是按圖像的灰度特性,將圖像分成背景和目標兩部分,或者說,是尋找一個閾值爲K,將圖像的顏色分爲1,2.....K和K+1.....256兩部分。

如何確定這個閾值K?算法分類的原理是讓背景和目標之間的類間方差最大,因爲背景和目標之間的類間方差越大,說明構成圖像的2部分的差別越大,錯分的可能性越小。下面進行公式推導:

首先是符號說明:對於圖像I(x,y),前景(即目標)和背景的分割閾值記作T,屬於前景的像素點數佔整幅圖像的比例記爲ω0,其平均灰度μ0;背景像素點數佔整幅圖像的比例爲ω1,其平均灰度爲μ1。圖像的總平均灰度記爲μ,類間方差記爲g。假設圖像的背景較暗,並且圖像的大小爲M×N,圖像中像素的灰度值小於閾值T的像素個數記作N0,像素灰度大於閾值T的像素個數記作N1。

\omega _{1}=\frac{N_{1}}{M\times N}

\omega _{2}=\frac{N_{2}}{M\times N}

N_{1}+N_{2}=M\times N

\omega _{1}+\omega _{2}=1

圖像的總平均灰度爲:\mu =\mu _{1}\times \omega _{1}+\mu _{2}\times \omega _{2}   (1)

前景和背景圖象的方差:g =\omega _{1}\times \left ( \mu-\mu _{1} \right )^{2}+\omega _{2}\times \left ( \mu-\mu _{2} \right )^{2}  (2)

將(1)代入(2)得:g =\omega _{1}\times \omega _{2}\times \left ( \mu_{1}-\mu _{2} \right )^{2}

採用遍歷的方法得到尋找到類間方差最大值,對應的閾值,即爲所求。

int Otsu(const IplImage *frame) //大津法求閾值
{
#define GrayScale 256    //frame灰度級
    int width = frame->width;
    int height = frame->height;
    int pixelCount[GrayScale]={0};
    float pixelPro[GrayScale]={0};
    int i, j, pixelSum = width * height, threshold = 0;
    uchar* data = (uchar*)frame->imageData;
 
    //統計每個灰度級中像素的個數
    for(i = 0; i < height; i++)
    {
        for(j = 0;j < width;j++)
        {
            pixelCount[(int)data[i * width + j]]++;
        }
    }
 
    //計算每個灰度級的像素數目佔整幅圖像的比例
    for(i = 0; i < GrayScale; i++)
    {
        pixelPro[i] = (float)pixelCount[i] / pixelSum;
    }
 
    //遍歷灰度級[0,255],尋找合適的threshold
    float w0, w1, u0tmp, u1tmp, u0, u1, deltaTmp, deltaMax = 0;
    for(i = 0; i < GrayScale; i++)
    {
        w0 = w1 = u0tmp = u1tmp = u0 = u1 = deltaTmp = 0;
        for(j = 0; j < GrayScale; j++)
        {
            if(j <= i)   //背景部分
            {
                w0 += pixelPro[j];
                u0tmp += j * pixelPro[j];
            }
            else   //前景部分
            {
                w1 += pixelPro[j];
                u1tmp += j * pixelPro[j];
            }
        }
        u0 = u0tmp / w0;
        u1 = u1tmp / w1;
        deltaTmp = (float)(w0 *w1* pow((u0 - u1), 2)) ;
        if(deltaTmp > deltaMax)
        {
            deltaMax = deltaTmp;
            threshold = i;
        }
    }
    return threshold;
}

opencv提供的閾值化方法有threshold和adaptiveThreshold,以及OTSU方法。 

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
 
int main(int argc, char* argv[])
{
	Mat img = imread(argv[1], -1);
	if (img.empty())
	{
		cout <<"Error: Could not load image" <<endl;
		return 0;
	}
 
	Mat gray;
	cvtColor(img, gray, CV_BGR2GRAY);
 
	Mat dst;
	threshold(gray, dst, 0, 255, CV_THRESH_OTSU);
 
	imshow("src", img);
	imshow("gray", gray);
	imshow("dst", dst);
	waitKey(0);
 
	return 0;
}

參考:

【1】https://baike.baidu.com/item/otsu/16252828?fr=aladdin

【2】Otsu N. A threshold selection method from gray-level histogram. IEEE Trans,1979;SMC-9;62-66

【3】https://blog.csdn.net/momo026/article/details/84026075

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章