自己实现图像阈值化(OTSU)
OTSU代码:
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
void OtusThreshold(Mat &src,Mat &dst)
{
dst=src.clone(); // 原图数据区指针
uchar* p_data=dst.data;
int nLineByte =dst.cols; // 图像每行像素所占的字节数
int nWidth =dst.cols; // 图像的宽度
int nHeight =dst.rows; // 图像的高度
// 灰度直方图数组,并初始化
int nGrayHistogram[256];
memset(nGrayHistogram,0,sizeof(nGrayHistogram));
// 统计各个灰度级对应的像素个数,并存放到灰度直方图数组中
int nPixel;
for (int j = 0; j < nHeight; j ++)
for (int i = 0; i < nWidth; i ++)
{
// 获取当前像素点的灰度值
nPixel = p_data[nLineByte * j + i];
//对灰度值统计计数
nGrayHistogram[nPixel] ++;
}
// c0组和c1组的均值
float u0, u1;
// c0组和c1组的概率
float w0, w1;
// c0组的像素总数
int nCount0;
// 阈值和最佳阈值(对应方差最大时的阈值)
int nT, nBestT;
// 方差和最大方差
float fVaria, fMaxVaria = 0;
// 统计直方图中像素点的总数,并存放到nSum中
int nSum=0;
for(int i = 0; i < 256; i ++)
nSum += nGrayHistogram[i];
// 令阈值nT从0遍历到255
for(nT = 0; nT < 256; nT ++)
{
// 当阈值为nT时,计算c0组的均值和概率
u0 = 0;
nCount0 = 0;
for(int i = 0; i <= nT; i++)
{
u0 += i * nGrayHistogram[i];
nCount0 += nGrayHistogram[i];
}
u0 /= nCount0;
w0 = (float) nCount0 / nSum;
// 当阈值为nT时,计算c1组的均值和概率
u1 = 0;
for(int i = nT+1; i < 256; i ++)
u1 += i * nGrayHistogram[i];
u1 /= (nSum - nCount0);
w1 = 1 - w0;
// 计算两组间的方差
fVaria = w0 * w1 * (u0 - u1) * (u0 - u1);
// 记录最大方差和最佳阈值
if(fVaria > fMaxVaria)
{
fMaxVaria = fVaria;
nBestT = nT;
}
}
// 利用最佳阈值对原图像作分割处理
for(int j = 0; j < nHeight; j ++)
for(int i = 0; i < nWidth; i ++)
{
if(p_data[j * nLineByte + i] < nBestT)
p_data[j * nLineByte + i] = 0;
else
p_data[j * nLineByte + i] = 255;
}
}
int main()
{
Mat src=imread("E:\\51.jpg");
Mat dst;
Mat bin;
cvtColor(src,dst,CV_BGR2GRAY);
imshow("灰度图像",dst);
OtusThreshold(dst,bin);
imshow("灰度二值化",bin);
imwrite("灰度图像的二值化.jpg",bin);
waitKey(0);
return 0;
}
运行结果: