OTSU 获取最佳阈值,及opencv二值化

Posted BigProgrambug

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OTSU 获取最佳阈值,及opencv二值化相关的知识,希望对你有一定的参考价值。


前言

OTSU算法,也就是大津算法,属于自适应的阈值确定方法,用该阈值进行的图像固定阈值二值化,类间方差最大,它是按图像的灰度特性,将图像分成背景和前景两部分,使类间方差最大的分割意味着错分概率最小。其核心思路是寻找一个阈值T,把图像的所有像素点分成两类,一类的像素值均小于等于T(背景区域),另一类的像素值均大于T(前景区域),当这两类的类间方差取得最大值时,则认为该T值为最合适的阈值。假设所有像素点的总和是Sum,背景区域所有像素点数为N1,其占图像总像素数的比例为w1,平均像素值为μ1;前景区域所有像素点数为N2,其占图像总像素数的比例为w2,平均像素值为μ2;μ 为0-255 的平均像素,那么有以下关系式:


提示:以下是本篇文章正文内容,下面案例可供参考

一、OTSU 算法求最佳阈值

有着上面的算法公式我们就可以很容易的把代码写出来了,下面是代码,如果不需要用QImage的话可以直接使用Mat,选哟 转换代码的可以去转换代码

int ImageHandle::GetOptimalThreshold(QImage &image)//OTSU (大津法)
{
	//首先先把QImage 转为三通道并转换为Mat
    image = image.convertToFormat(QImage::Format_RGB888);
    cv::Mat src = this->QImage2Mat(image);
    //灰度处理,转成单通道
    cv::cvtColor(src,src,cv::COLOR_BGR2GRAY);
    //图片的宽,高
    int nRows = src.rows;
    int nCols = src.cols;
	
	int threshold = 0;  //最佳阈值
    double temp = 0.0;    //获取
    double AvePix[256];
    int TotalPix[256];
    double nProDis[256];
    double nSumProDis[256];
    //变量初始化
    for (int i = 0; i < 256; i++)
    {
        AvePix[i] = 0.0;
        TotalPix[i] = 0;
        nProDis[i] = 0.0;
        nSumProDis[i] = 0.0;
    }
    //计算0-255 每个像素值的像素点
    for (int i = 0; i < nRows; i++)
    {
        for (int j = 0; j < nCols; j++)
        {
            TotalPix[(int)src.at<uchar>(i, j)]++;
        }
    }
    //计算每个像素值的像素点 占 总像素点的比例
    for (int i = 0; i < 256; i++)
    {
        nProDis[i] = (double)TotalPix[i] / (nRows*nCols);
    }

    AvePix[0] = 0;
    nSumProDis[0] = nProDis[0];
    //计算背景区域的像素比例 与 像素平均值
    for (int i = 1; i < 256; i++)
    {
        nSumProDis[i] = nSumProDis[i - 1] + nProDis[i];
        AvePix[i] = AvePix[i - 1] + i*nProDis[i];
    }

    double mean = AvePix[255];
    //遍历像素值 找到类间方差最大的像数值即为最佳阈值
    for (int i = 1; i < 256; i++)
    {
        double Pback = nSumProDis[i];
        double Pfront = 1 - nSumProDis[i];
        double g= 0.0;
        if (fabs(Pback ) > 0.000001 && fabs(Pfront ) > 0.000001)
        {
            double Mback = AvePix[i];
            double Mfront = (mean - Pback *Mback)/Pfront ;
            g= (double)(Pback * Pfront * pow((Mback - Mfront), 2));  //大津算法 即(背景像素占总像素的比例 * 前景像素占总像素的比例 * (背景平均像素 - 前景平均像素)的平方)
            if (g> temp)
            {
                temp = g;
                threshold = i;
            }
        }
    }
    return threshold;
}

二、使用获取的最佳阈值进行二值化

QImage ImageHandle::Binarization(QImage &image,int threshold)
{
    image = image.convertToFormat(QImage::Format_RGB888);
    cv::Mat src = this->QImage2Mat(image);
    cv::cvtColor(src,src,cv::COLOR_BGR2GRAY);
    cv::Mat dst;
    cv::threshold(src,dst,threshold,255,CV_THRESH_BINARY);
    return this->Mat2QImage(dst);
}


int main()
{
	
    QApplication a(argc, argv);
    QImage temp;
    temp = QImage("temp.png");
    ImageHandle f;
    int i = f.GetOptimalThreshold(temp);
    temp = f.Binarization(temp,i);
    QLabel *m_label = new QLabel();
    m_label->resize(1280,1024);
    QPixmap I_map = QPixmap::fromImage(temp);
    I_map = I_map.scaled(m_label->width(),m_label->height(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
    m_label->setPixmap(I_map);
    m_label->show();
    return a.exec();

}

三、效果

1、二值化前

2、二值化后

以上是关于OTSU 获取最佳阈值,及opencv二值化的主要内容,如果未能解决你的问题,请参考以下文章

2021-09-23 opencv学习笔记(图像变换,二值化,滤波器介绍及python实现)

S0.4 二值图与阈值化

OpenCV竟然可以这样学!成神之路终将不远

opencv-阈值分割

大津法---OTSU算法

《opencv学习》 之 二值化