算法原理
最大类间方差法是由日本学者大津于1979年提出的,是一种自适应的阈值确定法,又叫大津法,简称OTSU。
我用最简单的方式解释一下算法原理:
这个算法的思想就是假设阈值T将图像分成了前景和背景两个部分。
求出这两个部分的类间方差:
前景像素个数占比x(前景平均灰度 - 全图平均灰度)2 + 背景像素个数占比x(背景平均灰度 - 全图平均灰度)2
将阈值从0~255遍历一次,使上述类间方差最大的阈值T即为所求。
类间方差计算
我们接下来将算法原理中的类间方差化简为一个比较简单的形式,下面的过程也是代码实现的过程。
定义变量:
总像素数:N
前景像素数:fN
背景像素数:bN
前景像素灰度和:fSum
背景像素灰度和:bSum
前景像素平均灰度:fu
背景像素平均灰度:bu
图像总灰度值:Sum
图像平均灰度:u
前景像素占比:fw
背景图像占比:bw
类间方差:g
1
2
3
4
5
6
7
8
9
10
11
12
前景、背景占比:
前景像素占比:fw = fN/N
背景像素占比:bw = bN/N
1
2
前景、背景占比满足:
fw + bw =1 (1)
1
图片平均灰度值:
图片灰度直方图 : Histogram[256]
图片总灰度值 Sum : for(i=0; i<N; i++){ Sum += Histogram[i];}
图片平均灰度值 u : Sum/N
1
2
3
阈值为T时前景平均灰度:
阈值为T时前景像素数 fN : for(i=0; i<=T; i ++) { fN += Histogram[i];}
阈值为T时前景像素灰度和 fSum : for(i=0; i<=T; i++) { fSum += Histogram[i]*i;}
阈值为T时前景平均灰度 fu : fSum/fN
1
2
3
阈值为T时背景平均灰度:
阈值为T时背景像素数 bN : N-fN
阈值为T时背景像素灰度和 bSum : Sum-fSum
阈值为T时背景平均灰度 bu : bSum/bN
1
2
3
平均灰度满足:
u = fu*fw + bu*bw (2)
1
类间方差:
类间方差 g:fw*(fu-u)^2+bw*(bu-u)^2 (3)
1
将(1)(2)带入(3)式:
g = bw*fw*(fu-bu)^2
1
代码实现
假设读入图片为单通道Mat型灰度图,我们可以用以下代码实现:
#include <opencv2/opencv.hpp>
#include <cv.h>
#include <highgui.h>
#include <cxcore.h>
using namespace std;
using namespace cv;
Mat otsuGray(const Mat src) {
Mat img = src;
int c = img.cols; //图像列数
int r = img.rows; //图像行数
int T = 0; //阈值
uchar* data = img.data; //数据指针
int ftNum = 0; //前景像素个数
int bgNum = 0; //背景像素个数
int N = c*r; //总像素个数
int ftSum = 0; //前景总灰度值
int bgSum = 0; //背景总灰度值
int graySum = 0;
double w0 = 0; //前景像素个数占比
double w1 = 0; //背景像素个数占比
double u0 = 0; //前景平均灰度
double u1 = 0; //背景平均灰度
double Histogram[256] = {0}; //灰度直方图
double temp = 0; //临时类间方差
double g = 0; //类间方差
//灰度直方图
for(int i = 0; i < r ; i ++) {
for(int j = 0; j <c; j ++) {
Histogram[img.at<uchar>(i,j)]++;
}
}
//求总灰度值
for(int i = 0; i < 256; i ++) {
graySum += Histogram[i]*i;
}
for(int i = 0; i < 256; i ++) {
ftNum += Histogram[i]; //阈值为i时前景个数
bgNum = N - ftNum; //阈值为i时背景个数
w0 = (double)ftNum/N; //前景像素占总数比
w1 = (double)bgNum/N; //背景像素占总数比
if(ftNum == 0) continue;
if(bgNum == 0) break;
//前景平均灰度
ftSum += i*Histogram[i];
u0 = ftSum/ftNum;
//背景平均灰度
bgSum = graySum - ftSum;
u1 = bgSum/bgNum;
g = w0*w1*(u0-u1)*(u0-u1);
if(g > temp) {
temp = g;
T = i;
}
}
for(int i=0; i<img.rows; i++)
{
for(int j=0; j<img.cols; j++)
{
if((int)img.at<uchar>(i,j)>T)
img.at<uchar>(i,j) = 255;
else
img.at<uchar>(i,j) = 0;
}
}
return img;
————————————————
版权声明:本文为CSDN博主「Easy-Sir」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/a153375250/article/details/50970104