一维Otsu算法的原理与实现

来源:互联网 发布:杭州 大数据公司 编辑:程序博客网 时间:2024/06/05 18:39

1.简介:

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

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

2.算法思想:

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

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

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

3.算法过程:

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

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

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

                  (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为图片的行数,相当于高度,对应.yint width = srcimage.cols;     //cols为图片的列数,相当于宽度,对应.xlong 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.运行结果: