具體的公式推導參見岡薩雷斯***《數字影像處理》***
Otsu方法又稱最大類間方差法,通過把像素分配為兩類或多類,計算類間方差,當方差達到最大值時,類分割線(即灰度值)就作為影像分割閾值,Otsu還有一個重要的性質,即它完全基于對影像直方圖進行計算,這也使他成為最常用的閾值處理演算法之一,
演算法步驟如下:

Otsu只有在直方圖呈現雙峰的時候才會有一個很好的效果,在直方圖單峰或多峰的情況下效果不是很好,那就需要通過實際情況來選取其他的方法來得到預期的分割效果,
代碼如下;
int main()
{
string path = "F:\\Speedlimit\\RGBCutImages1\\14.jpg";
Mat SrcImage = imread(path);
if (!SrcImage.data) {
std::cout << "Could not open or find the image" << std::endl;
return -1;
}
cvtColor(SrcImage, SrcImage, COLOR_BGR2GRAY);
cv::Mat ThreImage, OtsuImage;
//直方圖引數定義
cv::Mat gray_hist; //float32
const int histSize = 256;
float range[] = { 0, 256 };
const float* histRange[] = { range };
bool uniform = true, accumulate = false;
calcHist(&SrcImage, 1, 0, Mat(), gray_hist, 1, &histSize, histRange, uniform, accumulate);
//獲得歸一化影像直方圖
cv::Mat norm_gray_hist = cv::Mat::zeros(gray_hist.size(), gray_hist.type());
for (int i = 0; i < histSize; ++i)
{
norm_gray_hist.at<float>(i) = gray_hist.at<float>(i) / SrcImage.total();
}
//全域均值和全域方差
Mat mat_mean, mat_stddev;
double gray_mean, gray_sigma;
meanStdDev(SrcImage, mat_mean, mat_stddev);
gray_mean = mat_mean.at<double>(0, 0);
//全域方差是用來計算類的可分離測度
gray_sigma = mat_stddev.at<double>(0, 0) * mat_stddev.at<double>(0, 0);
//遍歷所有灰度,計算類間方差
std::vector<double>sigma_ks(histSize);
for (int k = 0; k < histSize; ++k)
{
double p1 = 0.0; //p1類發生概率
double m_k = 0.0; //高達k階累計平均灰度
for (int i = 0; i <= k; ++i)
{
p1 += norm_gray_hist.at<float>(i);
m_k += i * norm_gray_hist.at<float>(i);
}
if (p1 == 0.f || (1 - p1) == 0.f) //分母不能為0
sigma_ks[k] = 0.0;
else
sigma_ks[k] = (gray_mean * p1 - m_k) * (gray_mean * p1 - m_k) / (p1 * (1 - p1));
}
double max_Sigma_k = 0.0;
std::vector<int>maxval_Ts;
int Threshold_T = 0; //最終輸出的閾值T
//找類間方差最大值
for (int i = 0; i < sigma_ks.size(); ++i)
{
if (sigma_ks[i] > max_Sigma_k)
max_Sigma_k = sigma_ks[i];
}
//找極大值對應的所有灰度值
for (int i = 0; i < sigma_ks.size(); ++i)
{
if (max_Sigma_k == sigma_ks[i])
maxval_Ts.push_back(i);
}
//如果極大值點不唯一,那么取對應各個極大值的各個k的平均值來得到最終閾值threshold_T
for (int i = 0; i < maxval_Ts.size(); ++i)
Threshold_T += maxval_Ts[i];
Threshold_T = Threshold_T / maxval_Ts.size();
cout << Threshold_T << endl;
//對比比較
threshold(SrcImage, ThreImage, Threshold_T, 255, THRESH_BINARY);
threshold(SrcImage, OtsuImage, 0, 255, THRESH_OTSU);
imshow("src", SrcImage);
cv::waitKey(0);
return 0;
}
處理結果:

新手上路,代碼僅作為參考,下一期更新***可使用掩模功能的大津閾值法***,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/342318.html
標籤:其他
下一篇:opencv+python識別豬
