图像分割之大津法Otsu

中文网上大津法的介绍很多,但是大多数给出的代码不是最优的而且存在分母为零的问题。

基本概念

        大津法(简称Otsu)由1979年由日本学者大津提出的,是一种自适应阈值确定的方法,相关文献链接。它是根据图像的灰度特性, 将图像分为前景和背景两个部分,当取最佳阈值时,二者之间的方差应该是最大的。论文精华如下。
图像分割之大津法Otsu_第1张图片
        论文中提到是基于Fisher线性判别(FLD)的,在Fisher线性判别式在类间方差(σB)和类内方差(σW)之间建立关系,其中,类间方差(σB)较大,而类内方差(σW)较小。如下图
图像分割之大津法Otsu_第2张图片
所以使σB最大或 σW最小的灰度值k。使σB最大更好些,因为σW计算更复杂些。

示例演示

        网上的实现代码都很相似,主要有两个问题:w作为分母,没有考虑w为零的情况;用了两层for循环,时间复杂度有点大。所以为了解决这些问题,自己实现了一遍,测试数据是经典图片coins。

#include 
#include 
using namespace std;
using namespace cv;

double MyOtsu(const Mat& image, const uint graylevel = 256)
{
	CV_Assert(image.type() == CV_8UC1);

	//computer Histogram
	std::vector histogram(graylevel, 0);
	for (int i = 0; i < image.rows; i++)
	{
		const uchar * p = image.ptr(i);
		for (int j = 0; j < image.cols; j++)
			histogram[int(*p++)]++; 
	}

	long pixelsum = 0;
	uint gray = 0;
	for (gray = 0; gray < graylevel; gray++)
		pixelsum += gray * histogram[gray];

	long pixelnum = image.cols * image.rows;
	//compute a weighted sum of variances of two classes
	long w0 = 0, w1 = 0;
	double maxDelta = 0, tempDelta = 0, sum0 = 0, mu0 = 0, mu1 = 0;
	int  threshold = 0;
	for (gray = 0; gray < graylevel; gray++)
	{
		w0 += histogram[gray];
		w1 = pixelnum - w0;
		sum0 += gray * histogram[gray];
		if (w0 == 0 || w1 == 0)
			continue;
		mu0 = sum0 / w0;
		mu1 = (pixelsum - sum0) / w1;
		tempDelta = w0 * w1 * (mu0 - mu1) * (mu0 - mu1);
		if (tempDelta >= maxDelta)
		{
			maxDelta = tempDelta;
			threshold = gray;
		}
	}
	return threshold;
}

int main()
{
	Mat img = imread("../../ImageData/coins.png");
	Mat src;
	cvtColor(img, src, CV_BGR2GRAY);

	cout << "The return value of MyOstu is: " << MyOtsu(src) << endl;

	//call the Otsu method of OpenCV
	Mat dst;
	cout << "The return value of OpenCV threshold is: " << threshold(src, dst, 0, 255, CV_THRESH_OTSU) << endl;
	imshow("Threshold Segment", dst);
	waitKey();

	return 0;

}

运行结果

图像分割之大津法Otsu_第3张图片
图像分割之大津法Otsu_第4张图片

你可能感兴趣的:(图像处理(OpenCV))