一维Otsu算法的原理与实现

1.简介:

           一维Otsu算法也叫最大类间方差法,是由日本学者大津(Nobuyuki Otsu)于1979年提出的,

 是一种图像灰度自适应阈值的分割算法,间称OTSU

2.算法思想:

        根据图像灰度值的特性,将图像分成背景和前景2个部分。背景和前景之间的类间方差越大,

说明构成图像的2部分的差别越大,当部分前景错分背景或者背景错分为前景的时候,会导致2部分的方差变小。

因此,类间方差最 大意味着错分概率最小。

3.算法过程:

                  1)对于图像I(x,y),将前景与背景的分割阈值设为T

                  (2)将属于前景的像素点的个数占整个图像的比例设为w0,其平均灰度设为u0

                  (3)将属于背景的像素点的个数占整个图像的比例设为w1,其平均灰度设为u1。

扫描二维码关注公众号,回复: 648227 查看本文章

                  (4)图像的总平均灰度设为u,类间方差设为S。

                   假设图片的大小为M*N,图像中像素灰度值小于阈值T的像素个数记为N0,

                   像素灰度大于阈值T的像素个数记为N1。则它们之间的关系如下。

                  

   


  

4.代码实现(opencv3):

#include "opencv.hpp"
#include "imgproc.hpp"
#include "highgui.hpp"
#include "iostream"
#include "core.hpp"

using namespace cv;
using namespace std;

int Otsu(Mat srcimage);           //一维Otsu算法
int main()
{
	Mat srcimage, grayimage, dstimage;
	srcimage = imread("lena.jpg");
	namedWindow("原图", 0);
	imshow("原图", srcimage);                                            //显示原图
	cvtColor(srcimage, grayimage, COLOR_RGB2GRAY);                         //得到灰度图
	//imshow("灰度图", grayimage);
	double time0 = static_cast<double>(getTickCount());                    //记录程序开始时间
	int thresholdValue = Otsu(grayimage);                                  //调用Otsu函数
	time0 = ((double)getTickCount() - time0) / getTickFrequency();
	cout << "算法运行时间为:" << time0 << endl;
	cout << "Otsu阈值为:" << thresholdValue << endl;
	threshold(grayimage, dstimage, thresholdValue, 255, THRESH_BINARY);    //将得到的阈值传入函数,得到分割效果图
	namedWindow("Otsu算法结果", 0);
	imshow("Otsu算法结果", dstimage);
	waitKey();
    return 0;
}

//一维Otsu算法
int Otsu(Mat srcimage)
{
	if (srcimage.channels() != 1)  //图片的通道数为1,即灰度图片
	{
		cout << "请输入灰度图片" << endl;
		return 0;
	}
	int height = srcimage.rows;    //rows为图片的行数,相当于高度,对应.y
	int width = srcimage.cols;     //cols为图片的列数,相当于宽度,对应.x
	long number = height*width;    //像素总数
	int T = 0;                     //Otsu算法阈值
	double varValue = 0;           //类间方差中间值
	double w1 = 0;                 //前景像素点所占比例
	double w2 = 0;                 //背景像素点所占比例
	double u1 = 0;                 //前景平均灰度
	double u2 = 0;                 //背景平均灰度
	int Histogram[256] = { 0 };    //灰度直方图,下标是灰度值,保存内容是灰度值对应的像素点总数。
	uchar *data = srcimage.data;
	for (int i = 0; i < height; i++)
	{
		for (int j = 0; j < width; j++)
		{
			Histogram[data[i*srcimage.step + j]]++;//step指向每行的字节总量,date访问每个像素的值
		}
	}
	for (int i = 1; i < 255 ;i++)//从1开始遍历,寻找最合适的值
	{
		//每次遍历前需要初始化各变量
		w1 = 0; u1 = 0; w2 = 0; u2 = 0;

		for (int j = 0; j <= i; j++)//背景部分各值计算  
		{
			w1 += Histogram[j];    //背景部分像素点总数  
			u1 += j*Histogram[j];  //背景部分像素总灰度和  
		}
		u1 = u1 / w1;              //背景像素平均灰度
		w1 = w1 / number;          //背景部分像素点所占比例

		for (int k = i + 1; k < 255; k++)
		{
			w2 += Histogram[k];    //前景部分像素点总数
			u2 += k*Histogram[k];  //前景部分像素总灰度和
		}	
		u2 = u2 / w2;              //前景像素平均灰度
		w2 = w2 / number;          //前景部分像素所占比例

		//类间方差计算
		double varValueI = w1*w2*(u1 - u2)*(u1 - u2);
		if ( varValueI>varValue)
		{
			varValue = varValueI;
			T = i;
		}
	}
	cout << T<<endl;
	return T;
}

5.运行结果:




猜你喜欢

转载自blog.csdn.net/kksc1099054857/article/details/78284887
今日推荐