图像分割:基于阈值(Otsu)

        图像分割算法的综述参考论文《图像分割方法综述_黄鹏》,知网上很方便查到下载,而且时间而言比较新。

        基于阈值的方法,最普遍的一个是二值化,将阈值以下和以上的分为两部分。应用比较广泛的有Otsu方法,可以计算出满足规则的最优阈值,规则一般采取类间方差的计算。当然还有其他的阈值选取规则方法。

        基于类间方差的方法自动获取最优阈值,再以此最优阈值进行分割。通过遍历每一个阈值,获得最优的类间方差值所对应的阈值作为最优解。因为是在同一项目中,只实现给定阈值计算类间方差的方法,最优阈值在初始化中遍历获取。

图像分割:基于阈值(Otsu)_第1张图片


 规则说明

        灰度级获取规则:采取0-255划分为256个等级,不进行低维度的划分。

        类间方差获取规则:

//类间方差计算规则
//w0	分开后前景像素点数占图像的比例
//u0	分开后前景像素点的平均灰度
//w1	分开后背景像素点数占图像的比例
//u1	分开后背景像素点的平均灰度
//方差值 返回w0*w1*(u0-u1)*(u0-u1)
//至于原理许多博客中均有提到 

实现

        由于从一个项目分出来的,带m_的是类的成员

灰度级函数

void ImgDivision::getGrayLevel()
{
    for(int y = 0; yheight(); y++)
    {
        QRgb * line = (QRgb *)m_Img->scanLine(y);
        for(int x = 0; xwidth(); x++)
        {
            int average = (qRed(line[x]) + qGreen(line[x]) + qBlue(line[x]))/3;
            m_GrayLevel[average] = m_GrayLevel[average] + 1;
        }
    }
}

获取类间方差函数
        返回该阈值下的方差值,因此后续在ImgDivision初始化时计算最优阈值,存在m_BestThreshold中。

double  ImgDivision::getInterclassVariance(int first_threshold){
    //w0    分开后前景像素点数占图像的比例
    double frontNums = 0.0;
    double frontGraySum = 0;
    for (int i = 0; i < first_threshold; i++) {
        frontNums = frontNums + m_GrayLevel[i];
        frontGraySum = frontGraySum + m_GrayLevel[i] * i;
    }
    double w0 = frontNums / (m_Img->width() * m_Img->height());
    //u0    分开后前景像素点的平均灰度
    double u0 = frontGraySum / frontNums;
    //w1    分开后背景像素点数占图像的比例
    double backNums = 0.0;
    double backGraySum = 0;
    for (int i = first_threshold; i < 256; i++) {
        backNums = backNums + m_GrayLevel[i];
        backGraySum = backGraySum + m_GrayLevel[i] * i;
    }
    double w1 = backNums / (m_Img->width() * m_Img->height());
    // u1    分开后背景像素点的平均灰度
    double u1 = backGraySum / backNums;
    return w0*w1*(u0-u1)*(u0-u1);
}

初始化中部分内容

//获取灰度级
    getGrayLevel();
    //计算类间方差 得到最优阈值
    double g = 0;
    for (int i = 0; i<256; i++) {
        if(g < getInterclassVariance(i)){
            g = getInterclassVariance(i);
            m_BestThreshold = i;
        }
    }

普通二值化函数

QImage* ImgDivision::byThreshold(int threshold)
{
    QImage *newImg = new QImage(m_Img->width(), m_Img->height(), QImage::Format_ARGB32);
    for(int y = 0; yheight(); y++)
    {
        QRgb * line = (QRgb *)m_Img->scanLine(y);
        for(int x = 0; xwidth(); x++)
        {
            int average = (qRed(line[x]) + qGreen(line[x]) + qBlue(line[x]))/3;
            if(average < threshold){
                newImg->setPixel(x,y, qRgb(255, 255, 255));
            }else {
                newImg->setPixel(x,y, qRgb(0, 0, 0));
            }
        }
    }
    return newImg;
}

        Otsu方法则只需要在调用普通二值化函数时,传入最优的阈值即可。

        主要内容是计算类间方差的实现。值得说明为了整体的方便,二值化并非是01,而是以0,255黑白两种像素进行区分,且都为RGB通道的。

        可以看效果以及其余内容: https://www.bilibili.com/read/cv12814828


你可能感兴趣的:(图像分割,图像处理,算法,人工智能)