OTSU算法是由日本学者OTSU于1979年提出的一种对图像进行二值化的高效算法,是一种自适应的阈值确定的方法,又称大津阈值分割法。
OTSU算法利用阈值将图像分为前景后背景两部分,使前景与背景之间的方差最大。
类间方差法对噪音和目标大小十分敏感,它仅对类间方差为单峰的图像产生较好的分割效果。
当目标与背景的大小比例悬殊时,类间方差准则函数可能呈现双峰或多峰,此时效果不好,但是类间方差法是用时最少的。
下面用c++和opencv分别进行了实现:
#include "pch.h"
#include
#include
using namespace cv;
using namespace std;
Mat my_otsu(Mat src) {
Mat dst;
src.copyTo(dst);
double varValue = 0; //类间方差中间值保存
int T = 0; //Otsu算法阈值
double Histogram[256] = { 0 };
int Histogram1[256] = { 0 };
//像素统计
for (int rows = 0; rows < src.rows; rows++) {
for (int cols = 0; cols < src.cols; cols++) {
int histogram = src.at(rows, cols);
Histogram[histogram]++;
Histogram1[histogram]++;
}
}
//直方图
Mat image1(255, 255, CV_8UC3);
for (int i = 0; i < 255; i++)
{
//Histogram1[i] = Histogram1[i] % 200;
Histogram1[i] = Histogram1[i] / 10;
line(image1, Point(i, 235), Point(i, 235 - Histogram1[i]), Scalar(255, 0, 0), 1, 8, 0);
if (i % 50 == 0)
{
char ch[255];
sprintf_s(ch, "%d", i);
string str = ch;
putText(image1, str, Point(i, 250), 1, 1, Scalar(0, 0, 255));
}
}
//求阈值g= w0 * w1*(u1 - u0)*(u1 - u0)
for (int t = 0; t < 256; t++) {
double w0 = 0; //前景像素点数所占比例
double w1 = 0; //背景像素点数所占比例
double u0 = 0; //前景平均灰度
double u1 = 0; //背景平均灰度
for (int i = 0; i < t; i++) {
w1 += Histogram[i];
u1 += i * Histogram[i];
}
u1 = u1 / w1;
for (int j = t + 1; j < 256; j++) {
w0 += Histogram[j];
u0 += j * Histogram[j];
}
u0 = u0 / w0;
double varValueI = w0 * w1*(u1 - u0)*(u1 - u0);
if (varValueI > varValue) {
varValue = varValueI;
T = t;
}
}
line(image1, Point(T, 235), Point(T, 0), Scalar(0, 0, 255), 2, 8);
imshow("直方图", image1);
//二值化
for (int rows = 0; rows < src.rows; rows++) {
for (int cols = 0; cols < src.cols; cols++) {
if (src.at(rows, cols) < T)
dst.at(rows, cols) = 0;
else if (src.at(rows, cols) >= T)
dst.at(rows, cols) = 255;
}
}
return dst;
}
int main()
{
Mat img = imread("C:/Users/45374/Desktop/code/img/lena.jpg");
Mat simg;
if (!img.data) {
cout << "erro" << endl;
return -1;
}
else {
imshow("input", img);
cvtColor(img, img, CV_RGB2GRAY);
threshold(img, simg, 0, 255, CV_THRESH_OTSU);
imshow("cv_otsu", simg);
Mat dst = my_otsu(img);
imshow("my_otsu", dst);
waitKey();
}
return 0;
}
效果如下: