平台:Windows 10 20H2
Visual Studio 2015
OpenCV 4.5.3

本文所用源码修改自C++ opencv 图片二值化最佳阈值确定(大津法,OTSU算法)——Sharon Liu

概念

 Otsu算法,也叫最大类间方差法,是1979年由日本学者大津提出的(所以也叫大津法),是一种自适应阈值确定的方法,一种全局的二值化算法。
        它是根据图像的灰度特性,将图像分为前景和背景两个部分。 当取最佳阈值时,两部分之间的差别应该是最大的。在Otsu算法中所采用的衡量差别的标准就是较为常见的最大类间方差。前景和背景之间的类间方差如果越大,就说明构成图像的两个部分之间的差别越大。
        当部分目标被错分为背景或部分背景被错分为目标,都会导致两部分差别变小。
        当所取阈值的分割使类间方差最大时,就意味着错分概率最小。
在这里插入图片描述

C++源码

OtsuThreshold

/******************************************************************************************
Function:       OtsuThreshold
Description:    图片二值化最佳阈值确定(大津法,OTSU算法)
Input:          src:原图片
Return:         阈值
******************************************************************************************/
int OtsuThreshold(Mat src)
{
    int threshold;

    try
    {
        int height = src.rows;
        int width = src.cols;

        //histogram  
        float histogram[256] = { 0 };
        for (int i = 0; i < height; i++) 
        {
            unsigned char* p = (unsigned char*)src.data + src.step*i;
            for (int j = 0; j < width; j++) 
            {
                histogram[*p++]++;
            }
        }
        //normalize histogram  
        int size = height*width;
        for (int i = 0; i < 256; i++) 
        {
            histogram[i] = histogram[i] / size;
        }

        //average pixel value  
        float avgValue = 0;
        for (int i = 0; i < 256; i++) 
        {
            avgValue += i*histogram[i];
        }

        float maxVariance = 0;
        float w = 0, u = 0;
        for (int i = 0; i < 256; i++) 
        {
            w += histogram[i];
            u += i*histogram[i];

            float t = avgValue*w - u;
            float variance = t*t / (w*(1 - w));
            if (variance > maxVariance) 
            {
                maxVariance = variance;
                threshold = i;
            }
        }
    }
    catch (cv::Exception e)
    {
    }

    return threshold;
}

主函数

图片路径根据实际情况调整,注意反斜杠是转义字符的开头,故“\”应替换为“\\”

int main(int argc, char * argv[])
{
    Mat Image = imread("D:\\Work\\OpenCV\\Workplace\\Test_1\\1.jpg", 0);

    int thresholdValue = OtsuThreshold(Image);
    cout << "类间方差为: " << thresholdValue << endl;

    Mat imageOutput;
    threshold(Image, imageOutput, thresholdValue, 255, CV_THRESH_BINARY);

    Mat imageOtsu;
    threshold(Image, imageOtsu, 0, 255, CV_THRESH_OTSU); //Opencv Otsu算法

    imshow("原图", Image);
    imshow("Output Image", imageOutput);
    imshow("Opencv Otsu", imageOtsu);

    waitKey(0);

    return 0;
}

效果

原图

在这里插入图片描述

效果

在这里插入图片描述

OpenCv自带的Otsu算法结果,与上图一致

在这里插入图片描述

完整源码

#include <opencv2\opencv.hpp>
#include <iostream>
#include <opencv2\imgproc\types_c.h>

using namespace cv;
using namespace std;


/******************************************************************************************
Function:       OtsuThreshold
Description:    图片二值化最佳阈值确定(大津法,OTSU算法)
Input:          src:原图片
Return:         阈值
******************************************************************************************/
int OtsuThreshold(Mat src)
{
    int threshold;

    try
    {
        int height = src.rows;
        int width = src.cols;

        //histogram  
        float histogram[256] = { 0 };
        for (int i = 0; i < height; i++) 
        {
            unsigned char* p = (unsigned char*)src.data + src.step*i;
            for (int j = 0; j < width; j++) 
            {
                histogram[*p++]++;
            }
        }
        //normalize histogram  
        int size = height*width;
        for (int i = 0; i < 256; i++) 
        {
            histogram[i] = histogram[i] / size;
        }

        //average pixel value  
        float avgValue = 0;
        for (int i = 0; i < 256; i++) 
        {
            avgValue += i*histogram[i];
        }

        float maxVariance = 0;
        float w = 0, u = 0;
        for (int i = 0; i < 256; i++) 
        {
            w += histogram[i];
            u += i*histogram[i];

            float t = avgValue*w - u;
            float variance = t*t / (w*(1 - w));
            if (variance > maxVariance) 
            {
                maxVariance = variance;
                threshold = i;
            }
        }
    }
    catch (cv::Exception e)
    {
    }

    return threshold;
}
//————————————————
//版权声明:本文为CSDN博主「Sharon Liu」的原创文章,遵循CC 4.0 BY - SA版权协议,转载请附上原文出处链接及本声明。
//原文链接:https ://blog.csdn.net/sylsjane/article/details/80872744

int main(int argc, char * argv[])
{
    Mat Image = imread("D:\\Work\\OpenCV\\Workplace\\Test_1\\1.jpg", 0);

    int thresholdValue = OtsuThreshold(Image);
    cout << "类间方差为: " << thresholdValue << endl;

    Mat imageOutput;
    threshold(Image, imageOutput, thresholdValue, 255, CV_THRESH_BINARY);

    Mat imageOtsu;
    threshold(Image, imageOtsu, 0, 255, CV_THRESH_OTSU); //Opencv Otsu算法

    imshow("原图", Image);
    imshow("Output Image", imageOutput);
    imshow("Opencv Otsu", imageOtsu);

    waitKey(0);

    return 0;
}