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

时间:2022-01-16 06:13:17

一、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;
}

效果图:

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

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

下面的两个图能直观的显示对应的阈值化类型的效果:
Opencv学习笔记-----图像阈值化处理
Opencv学习笔记-----图像阈值化处理
代码以及效果图如下:

#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;
}
不同类型的阈值化有不同的效果:
Opencv学习笔记-----图像阈值化处理


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

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