二值化系列:
(1)OTSU算法
(2)固定二值化
(3)自适应二值化
code:
#include<stdio.h>
#include<string>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/opencv.hpp>
using namespace std;
using namespace cv;
//OTSU实现函数
int OTSU(Mat srcImage)
{
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;
}
//统计灰度级(0-255)中每个像素在整幅图像中的个数
for (int i = 0; i < nRows; i++){
for (int j = 0; j < nCols; j++){
if ((int)srcImage.at<uchar>(i, j) < 0 || (int)srcImage.at<uchar>(i, j) >= 256){
cout << "yue jie" << endl;
system("pause");
}
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("F:\\opencv_re_learn\\2.jpg");
if (!srcImage.data){
cout << "failed to read" << endl;
system("pause");
return -1;
}
//转换为灰度图
Mat srcGray;
cvtColor(srcImage, srcGray, CV_RGB2GRAY);
imshow("gray", srcGray);
//调用OTSU函数计算阈值
int otsuThreshold = OTSU(srcGray);
cout << otsuThreshold << endl;
Mat threshold_result;
threshold(srcGray, threshold_result, otsuThreshold, 255, THRESH_BINARY);
imshow("oust_result", threshold_result);
waitKey(0);
return 0;
}
结果:
当然,也可以用opencv中自带的OTSU算法,至于两者的差别,需在具体情况下讨论。
threshold(srcGray, threshold_result, otsuThreshold, 255, THRESH_OTSU); 使用OTSU参数,会使得前面的阈值无效