Opencv學習筆記-----圖像閾值化處理

一、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;
}
不同類型的閾值化有不同的效果:



在這裏推薦一位大牛的主頁:

鄙人才疏學淺,如有疏漏還望指正。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章