一幅圖像包括目标、背景及噪聲,想要直接提取出目标物體,通常采用灰階變換門檻值化操作。圖像的門檻值化操作就是利用圖像像素點分布規律,設定門檻值進行像素點分割,進而得到二值化圖像。圖像門檻值化操作方法有很多,常用經典的有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二進制門檻值化為 對于THRESH_BINARY_INV反二進制門檻值化為 對于THRESH_TRUNC截斷門檻值化為 對于THRESH_TOZERO門檻值化為0為 對于THRESH_TOZERO_INV反門檻值化為0為 對于雙門檻值化為 對于半門檻值化為
<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)交叉相關高斯窗的權重總和),其門檻值是權重平均值,權重是區域内的高斯值,權重随着距離減小。