zoukankan      html  css  js  c++  java
  • 3.1.1 OTSU阈值化

    OTSU算法(大津法)是一种图像灰度自适应的阈值分割算法,是1979年由日本学者大津提出,并由他的名字命名的。大津法按照图像上灰度值的分布,将图像分成背景和前景两部分看待,前景就是我们要按照阈值分割出来的部分。背景和前景的分界值就是我们要求出的阈值。遍历不同的阈值,计算不同阈值下对应的背景和前景之间的类内方差,当类内方差取得极大值时,此时对应的阈值就是OTSU算法(大津法)所求的阈值。

    OTSU法(最大类间方差法,有时也称之为大津算法)使用的是聚类的思想。

    所以可以在二值化的时候 采用otsu算法来自动选取阈值进行二值化。otsu算法被认为是图像分割中阈值选取的最佳算法,计算简单,不受图像亮度和对比度的影响。因此,使类间方差最大的分割意味着错分概率最小。

    从L个灰度级遍历t,使得t为某个值的时候,前景和背景的方差最大, 则 这个 t 值便是我们要求得的阈值。

    其中,方差的计算公式如下:

    g=wo * (uo - u) * (uo - u) + w1 * (u1 - u) * (u1 - u)

    [ 此公式计算量较大,可以采用: g = wo * w1 * (uo - u1) * (uo - u1) ]

    //实现方法1:
    ////Source Code: https://blog.csdn.net/silent_gods/article/details/81046919
    //将OTSU算法实现了一遍
    
    #include "opencv2/opencv.hpp"
    #include "opencv2/highgui/highgui.hpp"
    
    #include <string>
    #include <stdio.h>
    
    using namespace std;
    using namespace cv;
    
    //#define Gamma 3
    
    //OTSU 函数实现
    int OTSU(Mat srcImage) 
    {
        int nCols = srcImage.cols;
        int nRows = srcImage.rows;
        int threshold = 0;
        //init the parameters
        int nSumPix[256];
        float nProDis[256];
        for (int i = 0; i < 256; i++)
        {
            nSumPix[i] = 0;
            nProDis[i] = 0;
        }
    
        //统计灰度集中每个像素在整幅图像中的个数
        for (int i = 0; i < nRows; i++)
        {
            for (int j = 0; j < nCols; j++)
            {
                nSumPix[(int)srcImage.at<uchar>(i, j)]++;
            }
        }
    
        //计算每个灰度级占图像中的概率分布
        for (int i = 0; i < 256; i++)
        {
            nProDis[i] = (float)nSumPix[i] / (nCols*nRows);
        }
    
        //遍历灰度级【0,255】,计算出最大类间方差下的阈值
    
        float w0, w1, u0_temp, u1_temp, u0, u1, delta_temp;
        double delta_max = 0.0;
        for (int i = 0; i < 256; i++)
        {
            //初始化相关参数
            w0 = w1 = u0 = u1 = u0_temp = u1_temp = delta_temp = 0;
            for (int j = 0; j < 256; j++)
            {
                //背景部分
                if (j <= i)
                {
                    w0 += nProDis[j];
                    u0_temp += j*nProDis[j];
                }
                //前景部分
                else
                {
                    w1 += nProDis[j];
                    u1_temp += j*nProDis[j];
                }
            }
            //计算两个分类的平均灰度
            u0 = u0_temp / w0;
            u1 = u1_temp / w1;
            //依次找到最大类间方差下的阈值
            delta_temp = (float)(w0*w1*pow((u0 - u1), 2)); //前景与背景之间的方差(类间方差)
            if (delta_temp > delta_max)
            {
                delta_max = delta_temp;
                threshold = i;
            }
        }
        return threshold;
    }
    
    
    int  main()
    {
    
        namedWindow("srcGray", 0);
        cvResizeWindow("srcGray", 640, 480);
        namedWindow("otsuResultImage", 0);
        cvResizeWindow("otsuResultImage", 640, 480);
        namedWindow("dst", 0);
        cvResizeWindow("dst", 640, 480);
        //图像读取及判断
        Mat srcImage;
        srcImage = imread("D:\0604.png");
        if (!srcImage.data)
        {
            return -1;
        }
        imshow("srcImage", srcImage);
        Mat srcGray;
        cvtColor(srcImage, srcGray, CV_RGB2GRAY);
        imshow("srcGray", srcGray);
    
        //调用otsu算法得到图像
        int otsuThreshold = OTSU(srcGray);
        cout << otsuThreshold << endl;
        //定义输出结果图像
        Mat otsuResultImage = Mat::zeros(srcGray.rows, srcGray.cols, CV_8UC1);
    
        //利用得到的阈值进行二值化操作
        for (int i = 0; i < srcGray.rows; i++)
        {
            for (int j = 0; j < srcGray.cols; j++)
            {
                //cout << (int)srcGray.at<uchar>(i, j) << endl;
                //高像素阈值判断
                if (srcGray.at<uchar>(i, j) > otsuThreshold)
                {
                    otsuResultImage.at<uchar>(i, j) = 255;
                }
                else
                {
                    otsuResultImage.at<uchar>(i, j) = 0;
                }
                //cout <<(int)otsuResultImage.at<uchar>(i, j) << endl;
            }
        }
        imshow("otsuResultImage", otsuResultImage);
        waitKey(0);
        return 0;
    }

    运行效果:

     1 ////实现方法2:https://blog.csdn.net/u012005313/article/details/51945075
     2 ////相对于实现方法1,方法2直接调用threshold(gray, dst, 0, 255, CV_THRESH_OTSU);
     3 #include <iostream>
     4 #include <opencv2/opencv.hpp>
     5 using namespace std;
     6 using namespace cv;
     7 
     8 int main(int argc, char* argv[])
     9 {
    10     //Mat img = imread(argv[1], -1);
    11     Mat img = imread("D:\0604.png");
    12     if (img.empty())
    13     {
    14         cout << "Error: Could not load image" << endl;
    15         return 0;
    16     }
    17 
    18     Mat gray;
    19     cvtColor(img, gray, CV_BGR2GRAY);
    20 
    21     Mat dst;
    22     threshold(gray, dst, 0, 255, CV_THRESH_OTSU);
    23 
    24     imshow("src", img);
    25     imshow("gray", gray);
    26     imshow("dst", dst);
    27     waitKey(0);
    28 
    29     return 0;
    30 }

    参考:

    代码:

    https://blog.csdn.net/silent_gods/article/details/81046919

    https://blog.csdn.net/u012005313/article/details/51945075

    原理:

    https://blog.csdn.net/liuzhuomei0911/article/details/51440305

    https://blog.csdn.net/xiachong27/article/details/80572469

    https://blog.csdn.net/u011574296/article/details/72829925

    https://baike.baidu.com/item/otsu/16252828?fr=aladdin

    https://www.cnblogs.com/uestc-mm/p/5366908.html

    https://blog.csdn.net/baimafujinji/article/details/50629103

  • 相关阅读:
    【Android Developers Training】 73. 布局变化的动画
    【Android Developers Training】 72. 缩放一个视图
    【Android Developers Training】 71. 显示翻牌动画
    svn更改地址怎么办
    python学习手册
    failed to bind pixmap to texture
    Ubuntu 12.04安装Google Chrome
    svn update 时总是提示 Password for '默认密钥' GNOME keyring: 输入密码
    重设SVN 的GNOME keyring [(null)] 的密码
    Nginx + uWSGI + web.py 搭建示例
  • 原文地址:https://www.cnblogs.com/thebreakofdawn/p/9463200.html
Copyright © 2011-2022 走看看