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

  • 个人简介:CSDN「博客新星」TOP 10 , C/C++ 领域新星创作者
  • 作    者:锡兰_CC ❣️
  • 专    栏:【OpenCV • c++】计算机视觉
  • 若有帮助,还请关注➕点赞➕收藏,不行的话我再努努力

文章目录

  • 前言
  • 一、大津法(OTSU)阈值化
    • 1.大津法算法步骤
      • 1)计算图像的直方图,统计每个像素在整幅图像中的个数。
      • 2)计算每个像素在整幅图像中的占比
      • 3)对灰度值进行遍历,统计前景像素)所占整幅图像的比例,平均灰度,背景像素所占整幅图像的比例,背景像素的平均灰度。
      • 4)计算图像的全局阈值
    • 2、代码演示
    • 3、运行效果

前言

通过上文的介绍,我们了解了三种图像,二值图像、灰度图像、彩色图像。三种图像的介绍。
在图像处理中,对于同样的操作,处理灰度图像的计算量要远远小于处理彩色图像,而二值图像(只含灰度值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
#include
#include 
#include 
#include 
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:\\Users\\86173\\Desktop\\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: 处理的有点吓人
【OpenCv • c++】 大津法(OTSU)阈值处理_第1张图片

其他:

更多专栏订阅:

  • 【开卷数据结构】
  • 【备战蓝桥,冲击省一】
  •    从零开始的 c++ 之旅
  • 【OpenCV • c++】计算机视觉

你可能感兴趣的:(【OpenCV】计算机视觉,c++,计算机视觉,opencv,图像处理)