【OpenCv • c++】 大津法(OTSU)阈值处理

简介: 【OpenCv • c++】 大津法(OTSU)阈值处理

前言


通过上文的介绍,我们了解了三种图像,二值图像、灰度图像、彩色图像。三种图像的介绍。

在图像处理中,对于同样的操作,处理灰度图像的计算量要远远小于处理彩色图像,而二值图像(只含灰度值0或1)的计算量比前两者更小。因此,二值化操作在图像处理中有着很大的作用。


二值化图像的实现方法有很多。用的最多的方法是利用图像像素点分布规律,设置阈值进行像素点分割,从而得到二值化图像。


一、大津法(OTSU)阈值化


在阈值处理中,最常用的方法就是大津法,因为其计算简单,不受图像亮度和对比度的影响。从大津法的原理上来讲,该方法又称作最大类间方差法,因为按照大津法求得的阈值进行图像二值化分割后,前景与背景图像的类间方差最大。


1.大津法算法步骤


1)计算图像的直方图,统计每个像素在整幅图像中的个数。


  //统计灰度级中每个像素在整幅图像中的个数
  for (int i = 0; i < nCols; i++) {
    for (int j = 0; j < nRows; j++) {
      nSumPix[(int)Image.at<uchar>(i, j)]++;
    }
  }


2)计算每个像素在整幅图像中的占比


  //计算每个灰度级占图像中的概率分布
  for (int i = 0; i < 256; i++) {
    nProDis[i] = (float)nSumPix[i] / (nCols * nRows);
  }


3)对灰度值进行遍历,统计前景像素)所占整幅图像的比例,平均灰度,背景像素所占整幅图像的比例,背景像素的平均灰度。


  //遍历灰度级[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_temp = u1_temp = u0 = u1 = delta_temp = 0;
    for (int j = 0; j < 256; j++) {
      //背景部分
      if (j <= i) {
        //当前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));
  }


4)计算图像的全局阈值


//依次找到最大类间方差下的阈值
  if (delta_temp > delta_max) {
    delta_max = delta_temp;
    threshold = i;
  }


2、代码演示


整体代码演示:

#include<stdio.h>
#include<string>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/types_c.h>
using namespace cv;
using namespace std;
int OTSU(cv::Mat srcImage)
{
  int nCols = srcImage.cols;
  int nRows = srcImage.rows;
  int threshold = 0;
  // 初始化统计参数
  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_temp = u1_temp = u0 = u1 = delta_temp = 0;
    for (int j = 0; j < 256; j++) {
      //背景部分 
      if (j <= i) {
        //当前i为分割阈值,第一类总的概率  
        w0 += nProDis[j];
        u0_temp += j * nProDis[j];
      }
      //前景部分   
      else {
        // 当前i为分割阈值,第一类总的概率
        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() {
  cv::Mat srcImage = cv::imread("...c.webp");
  if (!srcImage.data)
    return 1;
  cv::Mat srcGray;
  cv::cvtColor(srcImage, srcGray, CV_RGB2GRAY);
  cv::imshow("srcGray", srcGray);
  //利用 OTSU 二值化算法得到阈值
  int ostuThreshold = OTSU(srcGray);
  std::cout << ostuThreshold << endl;
  //定义输出结果图像
  cv::Mat otsuResultImage = cv::Mat::zeros(srcGray.rows, srcGray.cols, CV_8UC1);
  for (int i = 0; i < srcGray.rows; i++) {
    for (int j = 0; j < srcGray.cols; j++) {
      if (srcGray.at<uchar>(i, j) > ostuThreshold) {
        otsuResultImage.at<uchar>(i, j) = 255;
      }
      else {
        otsuResultImage.at<uchar>(i, j) = 0;
      }
    }
  }
  cv::imshow("otsuResultImage", otsuResultImage);
  cv::waitKey(0);
  return 0;
}


3、运行效果


PS: 处理的有点吓人

3cd97d98c5cb2a41b1cc48aa69b12208_9eb19ba07bd9491086db788abfc46ce3.png


相关文章
|
7月前
|
存储 算法 Linux
【实战项目】网络编程:在Linux环境下基于opencv和socket的人脸识别系统--C++实现
【实战项目】网络编程:在Linux环境下基于opencv和socket的人脸识别系统--C++实现
251 7
|
6月前
|
算法 开发工具 计算机视觉
【零代码研发】OpenCV实验大师工作流引擎C++ SDK演示
【零代码研发】OpenCV实验大师工作流引擎C++ SDK演示
98 1
|
2月前
|
Ubuntu Linux 编译器
Linux/Ubuntu下使用VS Code配置C/C++项目环境调用OpenCV
通过以上步骤,您已经成功在Ubuntu系统下的VS Code中配置了C/C++项目环境,并能够调用OpenCV库进行开发。请确保每一步都按照您的系统实际情况进行适当调整。
571 3
|
3月前
|
存储 计算机视觉 C++
在C++中实现Armadillo库与OpenCV库之间的数据格式转换
在C++中实现Armadillo库与OpenCV库之间的数据格式转换是一项常见且实用的技能。上述步骤提供了一种标准的方法来进行这种转换,可以帮助开发者在两个库之间高效地转移和处理数据。虽然转换过程相对直接,但开发者应留意数据类型匹配和性能优化等关键细节。
69 11
|
3月前
|
存储 计算机视觉 C++
在C++中实现Armadillo库与OpenCV库之间的数据格式转换
在C++中实现Armadillo库与OpenCV库之间的数据格式转换是一项常见且实用的技能。上述步骤提供了一种标准的方法来进行这种转换,可以帮助开发者在两个库之间高效地转移和处理数据。虽然转换过程相对直接,但开发者应留意数据类型匹配和性能优化等关键细节。
34 3
|
6月前
|
算法 计算机视觉
【Qt&OpenCV 图像阈值操作 threshold】
【Qt&OpenCV 图像阈值操作 threshold】
73 0
|
6月前
|
计算机视觉 C++
【见微知著】OpenCV中C++11 lambda方式急速像素遍历
【见微知著】OpenCV中C++11 lambda方式急速像素遍历
66 0
|
7月前
|
计算机视觉
OpenCV图像阈值
OpenCV图像阈值
31 0
|
7月前
|
算法 计算机视觉
【OpenCV】—阈值化
【OpenCV】—阈值化
|
7月前
|
人工智能 机器人 Linux
【C++】Linux下如何查看opencv的版本
【C++】Linux下如何查看opencv的版本
182 0