OTSU算法是由日本學者OTSU於1979年提出的一種對圖像進行二值化的高效算法,是一種自適應的閾值確定的方法,又稱大津閾值分割法。
OTSU算法利用閾值將圖像分爲前景後背景兩部分,使前景與背景之間的方差最大。
- 記t爲前景與背景的分割閾值,前景點數佔圖像比例爲w0,平均灰度爲u0;背景點數佔圖像比例爲w1,平均灰度爲u1。
- 則圖像的總平均灰度爲:u=w0*u0+w1*u1。
- 前景和背景圖象的方差:g=w0*(u0-u)*(u0-u)+w1*(u1-u)*(u1-u)=w0*w1*(u0-u1)*(u0-u1),此公式爲方差公式
- 尋找使方差最大的分割閾值作爲圖像二值化的閾值
類間方差法對噪音和目標大小十分敏感,它僅對類間方差爲單峯的圖像產生較好的分割效果。
當目標與背景的大小比例懸殊時,類間方差準則函數可能呈現雙峯或多峯,此時效果不好,但是類間方差法是用時最少的。
下面用c++和opencv分別進行了實現:
#include "pch.h"
#include<opencv2/opencv.hpp>
#include<iostream>
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<uchar>(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<uchar>(rows, cols) < T)
dst.at<uchar>(rows, cols) = 0;
else if (src.at<uchar>(rows, cols) >= T)
dst.at<uchar>(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;
}
效果如下: