原理:
OTSU算法也称最大类间差法,有时也称之为大津算法。前景与背景图像的类间方差最大。因方差是灰度分布均匀性的一种度量,背景和前景之间的类间方差越大,说明构成图像的两部分的差别越大,当部分前景错分为背景或部分背景错分为前景都会导致两部分差别变小。因此,使类间方差最大的分割意味着错分概率最小。Ostu方法可以形象地理解为:求取直方图有两个峰值的图像中那两个峰值之间的低谷值 T
参数:
属于前景的像素点数占整幅图像的比例记为w0,其平均灰度μ0;
背景像素点数占整幅图像的比例为w1,其平均灰度为μ1。
图像的总平均灰度记为μ:
μ=w0u0+w1u1.
类间方差记为delta:
delta=w0(u0-u)^2+w1(u1-u)^2
OTSU算法步骤:
1、统计灰度级中每个像素在整幅图像中的个数
2、计算每个像素在整幅图像的概率分布
3、对灰度级进行遍历搜索,计算当前灰度值下前景背景类间概率
4、通过目标函数计算类间方差下对应的阈值(选择最大方差对应的阈值)
我是分割线
代码:
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <string>
#include <stdio.h>
using namespace std;
using namespace cv;
#define Gamma 3
//OTSU 函数实现
int OTSU(Mat srcImage) {
int nCols = srcImage.cols;
int nRows = srcImage.rows;
int threshold = 0;
//init the parameters
int nSumPix[256];
float nProDis[256];
for (int i = 0; i < 256; i++)
{
nSumPix[i] = 0;
nProDis[i] = 0;
}
//统计灰度集中每个像素在整幅图像中的个数
for (int i = 0; i < nRows; i++)
{
for (int j = 0; j < nCols; 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 = u1 = u0_temp = u1_temp = delta_temp = 0;
for (int j = 0; j < 256; j++)
{
//背景部分
if (j <= i)
{
w0 += nProDis[j];
u0_temp += j*nProDis[j];
}
//前景部分
else
{
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()
{
namedWindow("srcGray", 0);
cvResizeWindow("srcGray", 640, 480);
namedWindow("otsuResultImage", 0);
cvResizeWindow("otsuResultImage", 640, 480);
namedWindow("dst", 0);
cvResizeWindow("dst", 640, 480);
//图像读取及判断
Mat srcImage;
srcImage = imread("image\\001.jpg");
if (!srcImage.data)
{
return -1;
}
Mat srcGray;
cvtColor(srcImage, srcGray, CV_RGB2GRAY);
imshow("srcGray", srcGray);
//调用otsu算法得到图像
int otsuThreshold = OTSU(srcGray);
cout << otsuThreshold << endl;
//定义输出结果图像
Mat otsuResultImage = Mat::zeros(srcGray.rows, srcGray.cols, CV_8UC1);
//利用得到的阈值进行二值化操作
for (int i = 0; i < srcGray.rows; i++)
{
for (int j = 0; j < srcGray.cols; j++)
{
//cout << (int)srcGray.at<uchar>(i, j) << endl;
//高像素阈值判断
if (srcGray.at<uchar>(i, j) > otsuThreshold)
{
otsuResultImage.at<uchar>(i, j) = 255;
}
else
{
otsuResultImage.at<uchar>(i, j) = 0;
}
//cout <<(int)otsuResultImage.at<uchar>(i, j) << endl;
}
}
imshow("otsuResultImage", otsuResultImage);
waitKey(0);
return 0;
}
版权声明:本文为silent_gods原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。