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

时间:2024-10-23 07:41:46

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

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

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


 规则说明

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

        类间方差获取规则:

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

实现

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

灰度级函数

  1. void ImgDivision::getGrayLevel()
  2. {
  3.     for(int y = 0; y<m_Img->height(); y++)
  4.     {
  5.         QRgb * line = (QRgb *)m_Img->scanLine(y);
  6.         for(int x = 0; x<m_Img->width(); x++)
  7.         {
  8.             int average = (qRed(line[x]) + qGreen(line[x]) + qBlue(line[x]))/3;
  9.             m_GrayLevel[average] = m_GrayLevel[average] + 1;
  10.         }
  11.     }
  12. }

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

  1. double  ImgDivision::getInterclassVariance(int first_threshold){
  2.     //w0    分开后前景像素点数占图像的比例
  3.     double frontNums = 0.0;
  4.     double frontGraySum = 0;
  5.     for (int i = 0; i < first_threshold; i++) {
  6.         frontNums = frontNums + m_GrayLevel[i];
  7.         frontGraySum = frontGraySum + m_GrayLevel[i] * i;
  8.     }
  9.     double w0 = frontNums / (m_Img->width() * m_Img->height());
  10.     //u0    分开后前景像素点的平均灰度
  11.     double u0 = frontGraySum / frontNums;
  12.     //w1    分开后背景像素点数占图像的比例
  13.     double backNums = 0.0;
  14.     double backGraySum = 0;
  15.     for (int i = first_threshold; i < 256; i++) {
  16.         backNums = backNums + m_GrayLevel[i];
  17.         backGraySum = backGraySum + m_GrayLevel[i] * i;
  18.     }
  19.     double w1 = backNums / (m_Img->width() * m_Img->height());
  20.     // u1    分开后背景像素点的平均灰度
  21.     double u1 = backGraySum / backNums;
  22.     return w0*w1*(u0-u1)*(u0-u1);
  23. }

初始化中部分内容

  1. //获取灰度级
  2.     getGrayLevel();
  3.     //计算类间方差 得到最优阈值
  4.     double g = 0;
  5.     for (int i = 0; i<256; i++) {
  6.         if(g < getInterclassVariance(i)){
  7.             g = getInterclassVariance(i);
  8.             m_BestThreshold = i;
  9.         }
  10.     }

普通二值化函数

  1. QImage* ImgDivision::byThreshold(int threshold)
  2. {
  3.     QImage *newImg = new QImage(m_Img->width(), m_Img->height(), QImage::Format_ARGB32);
  4.     for(int y = 0; y<m_Img->height(); y++)
  5.     {
  6.         QRgb * line = (QRgb *)m_Img->scanLine(y);
  7.         for(int x = 0; x<m_Img->width(); x++)
  8.         {
  9.             int average = (qRed(line[x]) + qGreen(line[x]) + qBlue(line[x]))/3;
  10.             if(average < threshold){
  11.                 newImg->setPixel(x,y, qRgb(255, 255, 255));
  12.             }else {
  13.                 newImg->setPixel(x,y, qRgb(0, 0, 0));
  14.             }
  15.         }
  16.     }
  17.     return newImg;
  18. }

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

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

        可以看效果以及其余内容: /read/cv12814828