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

效果如下:

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