zoukankan      html  css  js  c++  java
  • OTSU算法学习 OTSU公式证明

    OTSU算法学习   OTSU公式证明

     

    1 otsu的公式如下,如果当前阈值为t,

          w0 前景点所占比例

          w1 = 1- w0 背景点所占比例

          u0 = 前景灰度均值

          u1 = 背景灰度均值

          u = w0*u0 + w1*u1  全局灰度均值

          g = w0(u0-u)*(u0-u) + w1(u1-u)*(u1-u) = w0*(1 w0)*(u0 - u1)* (u0 - u1)

          目标函数为g, g越大,t就是越好的阈值.为什么采用这个函数作为判别依据,直观是这个函数反映了前景和背景的差值.

    差值越大,阈值越好.

    下面是一段证明g的推导的matlab代码

    syms w0 u0 u1 %w0 前景均值  u0 前景灰度均值 u1 背景灰度均值

    %背景均值

    w1 =1- w0;

    %全局灰度均值

    u=w0*u0+w1*u1;

    %目标函数

    g=w0*(u0-u)*(u0-u)+w1*(u1-u)*(u1-u);

    %化简的形式

    g1 =w0*w1*(u0-u1)*(u0-u1);

    %因式展开

    a1 = expand(g)%结果是 - u0^2*w0^2 + u0^2*w0 + 2*u0*u1*w0^2 - 2*u0*u1*w0 - u1^2*w0^2 + u1^2*w0

    a2 = expand(g)%结果是 - u0^2*w0^2 + u0^2*w0 + 2*u0*u1*w0^2 - 2*u0*u1*w0 - u1^2*w0^2 + u1^2*w0

    %g进行因式分解

    a2 = factor(g)%结果 -w0*(u0 - u1)^2*(w0 - 1)

     

    这里是matlab初等代数运算的讲解  http://wenku.baidu.com/link?url=SODqdtPjbNLhKPEvCjsHkOhMi9LMb34qIrnp9_QRBKUNqPLGLxRCuLJgL2sp1vhLk55b6hpp242-RTCVp6ma_7a7-0imT3WVyBcsTmQ-5HS

     

    2 关于最大类间方差法(otsu)的性能:

     

    类间方差法对噪音和目标大小十分敏感,它仅对类间方差为单峰的图像产生较好的分割效果。

    当目标与背景的大小比例悬殊时,类间方差准则函数可能呈现双峰或多峰,此时效果不好,但是类间方差法是用时最少的。

     

    3 代码实现

    public int GetThreshValue(Bitmap image)

    {

        BitmapData bd = image.LockBits(new Rectangle(0,0, image.Width, image.Height), ImageLockMode.WriteOnly, image.PixelFormat);

        byte* pt =(byte*)bd.Scan0;

        int[] pixelNum = new int[256];//图象直方图,共256个点

        byte color;

        byte* pline;

        int n, n1, n2;

        int total;//total为总和,累计值

        double m1, m2, sum, csum, fmax, sb;//sb为类间方差,fmax存储最大方差值

        int k, t, q;

        int threshValue =1;// 阈值

        int step =1;

        switch(image.PixelFormat)

        {

        case PixelFormat.Format24bppRgb:

            step =3;

            break;

        case PixelFormat.Format32bppArgb:

            step =4;

            break;

        case PixelFormat.Format8bppIndexed:

            step =1;

            break;

        }

        //生成直方图

        for(int i =0; i < image.Height; i++)

        {

            pline = pt + i * bd.Stride;

            for(int j =0; j < image.Width; j++)

            {

                color =*(pline + j * step);//返回各个点的颜色,以RGB表示

                pixelNum[color]++;//相应的直方图加1

            }

        }

        //直方图平滑化

        for(k =0; k <=255; k++)

        {

            total =0;

            for(t =-2; t <=2; t++)//与附近2个灰度做平滑化,t值应取较小的值

            {

                q = k + t;

                if(q <0)//越界处理

                    q =0;

                if(q >255)

                    q =255;

                total = total + pixelNum[q];//total为总和,累计值

            }

            //平滑化,左边2+中间1+右边2个灰度,共5个,所以总和除以5,后面加0.5是用修正值

            pixelNum[k]=(int)((float)total /5.0+0.5);

        }

        //求阈值

        sum = csum =0.0;

        n =0;

        //计算总的图象的点数和质量矩,为后面的计算做准备

        for(k =0; k <=255; k++)

        {

            //x*f(x)质量矩,也就是每个灰度的值乘以其点数(归一化后为概率),sum为其总和

            sum +=(double)k *(double)pixelNum[k];

            n += pixelNum[k];//n为图象总的点数,归一化后就是累积概率

        }

        fmax =-1.0;//类间方差sb不可能为负,所以fmax初始值为-1不影响计算的进行

        n1 =0;

        for(k =0; k <255; k++)//对每个灰度(从0255)计算一次分割后的类间方差sb

        {

            n1 += pixelNum[k];//n1为在当前阈值遍前景图象的点数

            if(n1 ==0){continue;}//没有分出前景后景

            n2 = n - n1;//n2为背景图象的点数

            //n20表示全部都是后景图象,与n1=0情况类似,之后的遍历不可能使前景点数增加,所以此时可以退出循环

            if(n2 ==0){break;}

            csum +=(double)k * pixelNum[k];//前景的灰度的值*其点数的总和

            m1 = csum / n1;//m1为前景的平均灰度

            m2 =(sum - csum)/ n2;//m2为背景的平均灰度

            sb =(double)n1 *(double)n2 *(m1 - m2)*(m1 - m2);//sb为类间方差

            if(sb > fmax)//如果算出的类间方差大于前一次算出的类间方差

            {

                fmax = sb;//fmax始终为最大类间方差(otsu

                threshValue = k;//取最大类间方差时对应的灰度的k就是最佳阈值

            }

        }

        image.UnlockBits(bd);

        image.Dispose();

        return threshValue;

    }

     

     

    参考了这篇文章 http://baike.baidu.com/link?url=ZgwdvFvH-oZhpE3panvp9kv0p7dn7KSnpc87v-AIBg5najnR4cVmIXwP_A_4nry7USDUZUuEYa-c5P09XOYoIa

     

    4 二维otsu算法

    下图是二维otsu的立体图,是对右边的A进行二维直方图统计得到的图像, 遍历区域为5*5.

     

    imageimage

     

                          

    这是对应的matlab代码

    %统计二维直方图  i 当前点的亮度 j n*n邻域均值亮度

    function hist2 = hist2Function(image, n)

     

    %初始化255*2565矩阵

    hist2 = zeros(255,255);

     

    [height, width]= size(image);

     

    for i =1:height

        for j =1:width

            data = image(i,j);

               

            tempSum =0.0;

            for l =-n:1:n

                for m =-n:1:n

                     x = i + l;

                     y = j+m;

                   

                    if x <1

                        x =1;

                    elseif x > width

                        x = width;

                    end

                   

                    if y <1

                        y =1;

                    elseif y > height

                        y = height;

                    end

                    tempSum = tempSum + double(image(y,x));

                end

            end

           

            tempSum = tempSum /((2*n+1)*(2*n+1));

           

            hist2(data,floor(tempSum))= hist2(data,floor(tempSum))+1;

        end

    end

     

    %加载图像

    imagea = imread('a.bmp');

    %显示图像

    %imshow(imagea);

    %显示直方图

    %figure;imhist(imagea);

    %计算二维直方图

    hist2 = hist2Function(imagea,2);

    %显示二维直方图

    [x,y]=meshgrid(1:1:255);

    mesh(x,y,hist2)

     

     

    <灰度图象的二维Otsu自动阈值分割法.pdf> 这篇文章讲解的不错.文章这里有下载http://download.csdn.net/detail/wisdomfriend/9046341

    下面用数学语言表达一下

    i :表示亮度的维度

    j : 表示点区域均值的维度

    w0: 表示在阈值(s,t)  所占的比例

    w1: 表示在阈值(s,t),  所占的比例

    u0(u0i, u0j): 表示在阈值(s,t)时时  的均.u02维的

    u1(u1i, u1j): 表示在阈值(s,t)的均值.u12维的

    uT: 全局均值

    和一维otsut函数类似的目标函数

     

    sb = w0(u0-uT)*(u0-uT) + w1(u1-uT)*(u1-uT)

       = w0[(u0i-uTi)* (u0i-uTi) + (u0j-uTj)* (u0j-uTj)] + w1[(u1i-uTi)* (u1i-uTi) + (u1j-uTj)* (u1j-uTj)]

     

    这里是代码实现.出自这篇文章:http://blog.csdn.net/yao_wust/article/details/23531031

     

    int histogram[256][256];

    double p_histogram[256][256];

    double Pst_0[256][256];//Pst_0用来存储概率分布情况

    double Xst_0[256][256];//存储x方向上的均值矢量

    int OTSU2d(IplImage * src)

    {

        int height = src->height;

        int width = src->width;

        long pixel = height * width;

       

        int i,j;

     

        for(i =0;i <256;i++)//初始化直方图

        {

            for(j =0; j <256;j++)

                histogram[i][j]=0;

        }

     

        IplImage * temp = cvCreateImage(cvGetSize(src),8,1);

        cvSmooth(src,temp,CV_BLUR,3,0);

       

        for(i =0;i < height;i++)//计算直方图

        {

            for(j =0; j < width;j++)

            {

                int data1 = cvGetReal2D(src,i,j);

                int data2 = cvGetReal2D(temp,i,j);

                histogram[data1][data2]++;

            }

        }

     

        for(i =0; i <256;i++)//直方图归一化

            for(j =0; j <256;j++)

                p_histogram[i][j]=(histogram[i][j]*1.0)/(pixel*1.0);

     

        Pst_0[0][0]= p_histogram[0][0];

        for(i =0;i <256;i++)//计算概率分布情况

            for(j =0;j <256;j++)

            {

               double temp =0.0;

               if(i-1>=0)

                   temp = temp + Pst_0[i-1][j];

               if(j-1>=0)

                   temp = temp + Pst_0[i][j-1];

               if(i-1>=0&& j-1>=0)

                   temp = temp - Pst_0[i-1][j-1];

               temp = temp + p_histogram[i][j];

               Pst_0[i][j]= temp;

            }

           

       

        Xst_0[0][0]=0* Pst_0[0][0];

        for(i =0; i <256;i++)//计算x方向上的均值矢量

            for(j =0; j <256;j++)

            {

               double temp =0.0;

               if(i-1>=0)

                   temp = temp + Xst_0[i-1][j];

               if(j-1>=0)

                   temp = temp + Xst_0[i][j-1];

               if(i-1>=0&& j-1>=0)

                   temp = temp - Xst_0[i-1][j-1];

               temp = temp + i * p_histogram[i][j];

               Xst_0[i][j]= temp;

            }

     

        double Yst_0[256][256];//存储y方向上的均值矢量

        Yst_0[0][0]=0* Pst_0[0][0];

        for(i =0; i <256;i++)//计算y方向上的均值矢量

            for(j =0; j <256;j++)

            {

               double temp =0.0;

               if(i-1>=0)

                   temp = temp + Yst_0[i-1][j];

               if(j-1>=0)

                   temp = temp + Yst_0[i][j-1];

               if(i-1>=0&& j-1>=0)

                   temp = temp - Yst_0[i-1][j-1];

               temp = temp + j * p_histogram[i][j];

               Yst_0[i][j]= temp;

            }

        

        int threshold1;

        int threshold2;

        double variance =0.0;

        double maxvariance =0.0;

        for(i =0;i <256;i++)//计算类间离散测度

            for(j =0;j <256;j++)

            {

                 longdouble p0 = Pst_0[i][j];

                 longdouble v0 = pow(((Xst_0[i][j]/p0)-Xst_0[255][255]),2)+ pow(((Yst_0[i][j]/p0)-Yst_0[255][255]),2);

                 longdouble p1 = Pst_0[255][255]-Pst_0[255][j]-Pst_0[i][255]+Pst_0[i][j];

                 longdouble vi = Xst_0[255][255]-Xst_0[255][j]-Xst_0[i][255]+Xst_0[i][j];

                 longdouble vj = Yst_0[255][255]-Yst_0[255][j]-Yst_0[i][255]+Yst_0[i][j];

                 longdouble v1 = pow(((vi/p1)-Xst_0[255][255]),2)+pow(((vj/p1)-Yst_0[255][255]),2);

               

                 variance = p0*v0+p1*v1;

              

               if(variance > maxvariance)

               {

                  maxvariance = variance;

                  threshold1 = i;

                  threshold2 = j;

               }

            }

     

        //printf("%d  %d",threshold1,threshold2);

     

        return(threshold1+threshold2)/2;

     

    }

     

     

    总结: 二维otsu算法得到一个阈值,然后对图像做二值化.仍然不能解决光照不均匀二值化的问题.比一维otsu效果好一些,但不是很明显.这个算法的亮点在于考虑的点的附近区域的均值.

     

  • 相关阅读:
    java_day12_jdk1.8新特性
    IT修养-基础篇
    selenium之 chromedriver与chrome版本映射表(更新至v2.31)
    selenium--定位--CSS
    敏捷测试--之scrum--原理
    敏捷测试和瀑布测试的关联
    bugfree安装
    Linux
    mysql--命令行创建数据库
    软件测试流程
  • 原文地址:https://www.cnblogs.com/guopengfei/p/4759569.html
Copyright © 2011-2022 走看看