平台:Windows 10 20H2 Visual Studio 2015 OpenCV 4.5.3
本文所用源码修改自C++ opencv 图片二值化最佳阈值确定(大津法,OTSU算法)——Sharon Liu
概念
????????Otsu算法,也叫最大类间方差法,是1979年由日本学者大津提 出的(所以也叫大津法),是一种自适应阈值确定的方法,是一种全局的二值化算法。 ????????它是根据图像的灰度特性,将图像分为前景和背景两个部分。 当取最佳阈值时,两部分之间的差别应该是最大的。在Otsu算法中所采用的衡量差别的标准就是较为常见的最大类间方差。前景和背景之间的类间方差如果越大,就说明构成图像的两个部分之间的差别越大。 ????????当部分目标被错分为背景或部分背景被错分为目标,都会导致两部分差别变小。 ????????当所取阈值的分割使类间方差最大时,就意味着错分概率最小。
C++源码
OtsuThreshold
int OtsuThreshold(Mat src)
{
int threshold;
try
{
int height = src.rows;
int width = src.cols;
float histogram[256] = { 0 };
for (int i = 0; i < height; i++)
{
unsigned char* p = (unsigned char*)src.data + src.step*i;
for (int j = 0; j < width; j++)
{
histogram[*p++]++;
}
}
int size = height*width;
for (int i = 0; i < 256; i++)
{
histogram[i] = histogram[i] / size;
}
float avgValue = 0;
for (int i = 0; i < 256; i++)
{
avgValue += i*histogram[i];
}
float maxVariance = 0;
float w = 0, u = 0;
for (int i = 0; i < 256; i++)
{
w += histogram[i];
u += i*histogram[i];
float t = avgValue*w - u;
float variance = t*t / (w*(1 - w));
if (variance > maxVariance)
{
maxVariance = variance;
threshold = i;
}
}
}
catch (cv::Exception e)
{
}
return threshold;
}
主函数
图片路径根据实际情况调整,注意反斜杠是转义字符的开头,故“\”应替换为“\\”
int main(int argc, char * argv[])
{
Mat Image = imread("D:\\Work\\OpenCV\\Workplace\\Test_1\\1.jpg", 0);
int thresholdValue = OtsuThreshold(Image);
cout << "类间方差为: " << thresholdValue << endl;
Mat imageOutput;
threshold(Image, imageOutput, thresholdValue, 255, CV_THRESH_BINARY);
Mat imageOtsu;
threshold(Image, imageOtsu, 0, 255, CV_THRESH_OTSU);
imshow("原图", Image);
imshow("Output Image", imageOutput);
imshow("Opencv Otsu", imageOtsu);
waitKey(0);
return 0;
}
效果
原图 效果 OpenCv自带的Otsu算法结果,与上图一致
完整源码
#include <opencv2\opencv.hpp>
#include <iostream>
#include <opencv2\imgproc\types_c.h>
using namespace cv;
using namespace std;
int OtsuThreshold(Mat src)
{
int threshold;
try
{
int height = src.rows;
int width = src.cols;
float histogram[256] = { 0 };
for (int i = 0; i < height; i++)
{
unsigned char* p = (unsigned char*)src.data + src.step*i;
for (int j = 0; j < width; j++)
{
histogram[*p++]++;
}
}
int size = height*width;
for (int i = 0; i < 256; i++)
{
histogram[i] = histogram[i] / size;
}
float avgValue = 0;
for (int i = 0; i < 256; i++)
{
avgValue += i*histogram[i];
}
float maxVariance = 0;
float w = 0, u = 0;
for (int i = 0; i < 256; i++)
{
w += histogram[i];
u += i*histogram[i];
float t = avgValue*w - u;
float variance = t*t / (w*(1 - w));
if (variance > maxVariance)
{
maxVariance = variance;
threshold = i;
}
}
}
catch (cv::Exception e)
{
}
return threshold;
}
int main(int argc, char * argv[])
{
Mat Image = imread("D:\\Work\\OpenCV\\Workplace\\Test_1\\1.jpg", 0);
int thresholdValue = OtsuThreshold(Image);
cout << "类间方差为: " << thresholdValue << endl;
Mat imageOutput;
threshold(Image, imageOutput, thresholdValue, 255, CV_THRESH_BINARY);
Mat imageOtsu;
threshold(Image, imageOtsu, 0, 255, CV_THRESH_OTSU);
imshow("原图", Image);
imshow("Output Image", imageOutput);
imshow("Opencv Otsu", imageOtsu);
waitKey(0);
return 0;
}
|