otsu閾值分割原理及實現

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;
}

效果如下:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章