一、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>上面函数就是采用的THRESH_OTSU方式,即Opencv封装好的大津算法的二值化。
#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;
}
当然,也可以添加一个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
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;
}
在这里推荐一位大牛的主页
:
鄙人才疏学浅,如有疏漏还望指正。