天天看點

opencv圖像處理學習(九)——門檻值化

一幅圖像包括目标、背景及噪聲,想要直接提取出目标物體,通常采用灰階變換門檻值化操作。圖像的門檻值化操作就是利用圖像像素點分布規律,設定門檻值進行像素點分割,進而得到二值化圖像。圖像門檻值化操作方法有很多,常用經典的有OTUS、固定門檻值、自适應門檻值、雙門檻值及半門檻值化操作。

<1>OTUS

其算法步驟如下:

(1)統計灰階級中每一個像素在整幅圖像中的個數

(2)計算每個像素在整幅圖像的機率分布

(3)對灰階級進行周遊搜尋,計算目前灰階值下前景背景類間機率

(4)通過目标函數計算出類内與類間方差下對應的門檻值

e.g:

#include <stdio.h>
#include <string>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\opencv.hpp>

using namespace std;
using namespace cv;

int OTUS(Mat srcImage)//Image應該是一個8U的圖像
{
    int nCols = srcImage.cols;
    int nRows = srcImage.rows;
    int threshold = 0;

    //初始化統計參數

    int nSumPix[256];//像素個數
    float nProDis[256];//機率分布

    for (int i = 0; i < 256; i++)
    {
        nSumPix[i] = 0;
        nProDis[i] = 0;
    }

    //統計灰階級中每個像素在整幅圖像中的個數
    for (int i = 0; i < nCols; i++)
    {
        for (int j = 0; j < nRows; j++)
        {
            nSumPix[(int)srcImage.at<uchar>(i, j)]++;
        }
    }

    //統計每個灰階級占圖像中的機率分布
    for (int i = 0; i < 256; i++)
    {
        nProDis[i] = (float)nSumPix[i] / (nCols * nRows);
    }

    //周遊灰階級[0,255],計算出最大類間方差下的門檻值
    float w0, w1, u0_temp, u1_temp, u0, u1, delta_temp;
    double delta_max = 0.0;

    for (int i = 0; i < 256; i++)
    {
        w0 = w1 = u0_temp = u1_temp = u0 = u1 = delta_temp = 0;
        
        for (int j = 0; j < 256; j++)
        {
            //背景部分
            if ( j <= i)
            {
                //目前i為分割門檻值,第一類總機率
                w0 += nProDis[j];
                u0_temp += j * nProDis[j];
            }

            //前景部分
            else
            {
                //目前i為分割門檻值,第一類總機率
                w1 += nProDis[j];
                u1_temp += j * nProDis[j];

            }
        }

        //分别計算各類的平均灰階
        u0 = u0_temp / w0;
        u1 = u1_temp / w1;
        delta_temp = (float)( w0 * w1 * pow((u0 - u1), 2));

        //依次找到最大類間方差下的門檻值
        if (delta_temp > delta_max)
        {
            delta_max = delta_temp;
            threshold = i;
        }
    }

    return threshold;
}

int main()
{
    Mat srcImage = imread("u=497076178,1248191674&fm=26&gp=0.jpg");
    
    if (!srcImage.data)
    {
        return -1;
    }

    Mat srcGray;
    cvtColor(srcImage, srcGray,CV_RGB2GRAY);
    imshow("srcGray", srcGray);

    int outThreshold = OTUS(srcGray);
    std::cout << outThreshold << std::endl;

    Mat otusResultImage = cv::Mat::zeros(srcGray.rows, srcGray.cols,CV_8UC1);

    //調用OTUS函數
    for (int i = 0; i < srcGray.rows; i++)
    {
        for (int j = 0; j < srcGray.cols; j++)
        {
            if (srcGray.at<uchar>(i,j) > outThreshold)
            {
                otusResultImage.at<uchar>(i, j) = 255;

            }

            else
            {
                otusResultImage.at<uchar>(i, j) = 0;
            }
        }
    }

    imshow("otusResultImage", otusResultImage);
    waitKey(0);
    return 0;
}
           

PS:這個程式有個bug沒找到,有哪個大神願意試一下說一下問題的

<2>其他門檻值化

opencv中提供的門檻值化為 double threshold(InputArray src,Output dst,double thresh,double maxval,int type);

其中參數src表示源圖像,dst表示輸出圖像,thresh表示門檻值設定,maxval表示預設最大值,使用THRESH_BINARY或THRESH_BINARY_INY類型;type表示門檻值化處理的類型設定。

PS:該函數應用在單通道圖像中固定門檻值化處理,通常是為了得到二值化圖像或除去噪聲。

對于THRESH_BINARY二進制門檻值化為
opencv圖像處理學習(九)——門檻值化
對于THRESH_BINARY_INV反二進制門檻值化為
opencv圖像處理學習(九)——門檻值化
對于THRESH_TRUNC截斷門檻值化為
opencv圖像處理學習(九)——門檻值化
對于THRESH_TOZERO門檻值化為0為
opencv圖像處理學習(九)——門檻值化
對于THRESH_TOZERO_INV反門檻值化為0為
opencv圖像處理學習(九)——門檻值化
對于雙門檻值化為
opencv圖像處理學習(九)——門檻值化
對于半門檻值化為
opencv圖像處理學習(九)——門檻值化

<3>自适應門檻值化

在opencv中,其函數名為adaptiveThreshold.

void adaptiveThreshold(InputArray src,OutputArray dst, double maxval,int adaptiveMethod,int thresholdTpye,int blocksize,double C);

PS:大部分人對這個常數C很懵,我們下面來具體講解

其中src表示源圖像;dst表示輸出圖像;maxval表示預設滿足條件的最大值;adaptiveMethod表示自适應門檻值算法的選擇,一共有兩種;ThresholdType表示門檻值類型;參數blockSize表示鄰域塊大小;C表示常數。

首先blockSize必須為奇數(3,5,7……),這個拿來做掩膜(PS:後面再講),這個參數小則具有邊緣檢測的效果,大則達到二值化效果。

現在我們來看看常數C到底幹嘛的。PS:有的文章說是控制邊緣的類型和粗細。

上面提到了自适應門檻值化的兩種方法,其分别為ADAPTIVE_THRESH_MEAN_C和ADAPTIVE_THRESH_GAUSSIAN_C

對于ADAPTIVE_THRESH_MEAN_C,其數學運算為:領域塊内所有像素-blocksize*常數C,其門檻值是區域内的均值。

對于ADAPTIVE_THRESH_GAUSSIAN_C,其數學運算為:blocksize*((領域塊内所有像素-C)交叉相關高斯窗的權重總和),其門檻值是權重平均值,權重是區域内的高斯值,權重随着距離減小。