Opencv学习笔记-----图像阈值化处理

本文介绍了OTSU阈值化处理方法,包括非API实现原理及代码实现,并对比了多种阈值化处理方式如THRESH_BINARY等的效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、OTSU阈值化处理(非API实现)

OTSU又称大津算法,是nobuyuki otsu于1979年提出的一种寻找图像阈值的最大类间方差算法。
OTSU算法的步骤如下:
         (1)、统计灰度级[0,255]中每个像素在整幅图像中的个数。
         (2)、计算每个像素在整个灰度级的分布情况。
         (3)、对整个灰度级遍历,计算当前灰度值下的前、背景类间概率。
         (4)、计算出类内与类间方差下的对应的阈值。

1、非API方式实现OTSU算法:

代码如下:
#include <opencv2\opencv.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <stdio.h>
#include <string.h>
using namespace std;
using namespace cv;

int OTSU(Mat src)
{
	int nRows = src.rows;
	int nCols = src.cols;
	int threshold = 0;
	//初始化统计参数
	int nSumpix[256];
	float nDis[256];
	for (int i = 0; i < 256; i++)
	{
		nSumpix[i] = 0;
		nDis[i] = 0;
	}
	//统计灰度级中每个像素在整幅图的个数
	for (int i = 0; i < nRows; i++)
	{
		for (int j = 0; j < nCols; j++)
		{
			nSumpix[(int)src.at<uchar>(i, j)]++;
		}
	}
	
	//计算每个灰度级占图像中的分布
	for (int i = 0; i < 256; i++)
	{
		nDis[i] = (float)nSumpix[i] / (nRows*nCols);
	}

	//遍历灰度级[0,255],计算最大类方差的阈值
	float w0, w1, u0_temp, u1_temp, u0, u1, delta;
	double delta_max = 0.0;
	for (int i = 0; i < 256; i++)
	{
		//初始化参数
		w0 = w1 = u0_temp = u1_temp = u0 = u1 = delta = 0;
		for (int j = 0; j < 256; j++)
		{
			if (j <= i)
			{
				//当前i为分割阈值
				w0 += nDis[j];
				u0_temp += j * nDis[j];
			}
			//前景部分
			else
			{
				//当前i为分割阈值
				w1 += nDis[j];
				u1_temp += j * nDis[j];
			}
		}
		//分别计算各类平均值
		u0 = u0_temp / w0;
		u1 = u1_temp / w1;
		delta = (float)(w0*w1*pow((u0 - u1), 2));
		//依次寻找最大类间方差的阈值
		if (delta>delta_max)
		{
			delta_max = delta;
			threshold = i;
		}
	}
	return threshold;
}

int main(int argc, char* argv[])
{
	Mat src = imread(".//res//num.jpg");

	if (!src.data)
		return -1;
	else
		imshow("src", src);

	Mat srcGray;
	cvtColor(src, srcGray, CV_BGR2GRAY);
	//调用UTSO得到阈值
	int otsuThreshold = OTSU(srcGray);

	Mat dst(srcGray.size(), CV_8UC1);

	for (int i = 0; i < srcGray.rows; i++)
	{
		for (int j = 0; j < srcGray.cols; j++)
		{
			if (srcGray.at<uchar>(i, j) > otsuThreshold)
				dst.at<uchar>(i, j) = 255;
			else
				dst.at<uchar>(i, j) = 0;	
		}
	}
	imshow("OTSU", dst);
	waitKey(0);
	return 0;
}

效果图:


         

2、Opencv的API方式实现:

当然经典的算法也有加入到Opencv库中,下面即是直接调用API的方式实现OTSU阈值化操作:
使用的函数是double threshold (InputArray src,  OutputArray dst,  double thresh,  double maxval,  int type)
src      --> 输入的图像(灰度图)
dst      -->  输出的图像
thresh --> 二值化的阈值
maxval-->用于设定THRESH_BINARY和THRESH_BINARY_INV阈值类型的最大阈值
#include <opencv2\opencv.hpp>
#include <opencv2\highgui\highgui.hpp>
using namespace std;
using namespace cv;

int main()
{
	Mat src = imread(".//res//num.jpg");
	cvtColor(src, src, CV_BGR2GRAY);
	Mat dst;
	threshold(src, dst, 167, 255, THRESH_OTSU);
	imshow("dst", dst);
	waitKey();
	return 0;
}
上面函数就是采用的THRESH_OTSU方式,即Opencv封装好的大津算法的二值化。
当然,也可以添加一个TrackBar来实时调整阈值(当前为固定的167)。

二、其他方式的阈值化处理


THRESH_BINARY,表示dsti=(srci>T)?M:0
THRESH_BINARY_INV,表示dsti=(srci>T)?0:M
THRESH_TRUNC,表示dsti=(srci>T)?M:srci
THRESH_TOZERO_INV,表示dsti=(srci>T)?0:srci
THRESH_TOZERO,表示dsti=(srci>T)?srci:0

下面的两个图能直观的显示对应的阈值化类型的效果:


代码以及效果图如下:

#include <opencv2\opencv.hpp>
#include <opencv2\highgui\highgui.hpp>
using namespace std;
using namespace cv;

int main()
{
	Mat src = imread(".//res//num.jpg");
	imshow("src", src);
	cvtColor(src, src, CV_BGR2GRAY);

	Mat otsu;
	threshold(src, otsu, 167, 255, THRESH_OTSU);
	imshow("otsu", otsu);

	Mat binary;
	threshold(src, binary, 167, 255, THRESH_BINARY);
	imshow("binary", binary);

	Mat binary_inv;
	threshold(src, binary_inv, 167, 255, THRESH_BINARY_INV);
	imshow("binary_inv", binary_inv);

	Mat trunc;
	threshold(src, trunc, 167, 255, THRESH_TRUNC);
	imshow("trunc", trunc);

	Mat tozero_inv;
	threshold(src, tozero_inv, 167, 255, THRESH_TOZERO_INV);
	imshow("tozero_inv", tozero_inv);

	Mat tozero;
	threshold(src, tozero, 167, 255, THRESH_TOZERO);
	imshow("tozero", tozero);

	waitKey();
	return 0;
}
不同类型的阈值化有不同的效果:



在这里推荐一位大牛的主页 :

鄙人才疏学浅,如有疏漏还望指正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值