利用OpenCV实现OTSU分割和最大熵分割
Otsu分割
一、 原理
我们利用otsu分割算法可以对一张图片的前景和背景进行分割。其思想主要是寻找前景与背景之间的界限灰度值(大于该界限灰度值的部分为前景,小于),前景的平均灰度值减去图像的平均灰度值的平方记为k0,背景的平均灰度值减去图像的平均灰度值的平方记为k1,此时将前景所占的图像百分比m0乘以k0在加上背景所占的百分比m1乘以k1,记相加和为s。这里的关键是选定界定背景和前景的界定灰度值,我们并不知道这个灰度值是多少,所以我们需要一个一个灰度值的试,试出来最后选取s最大的即为真正的界定灰度值。
二、 代码实现
//通过OTSU算法来确定阈值
//该算法需要的参数有:前景(背景)像素比例;前景/背景平均灰度值;类间方差公式:g=w0*w0*(u0-u1)(u0-u1)
void OTSU(Mat srcImage) {
int cols = srcImage.cols;
int rows = srcImage.rows;
int thresh = 0;
Mat GrayImage;
cvtColor(srcImage, GrayImage, COLOR_BGR2GRAY);
//初始化统计参数
int sumpix[256];
float prodis[256];
for (int i = 0; i < 256; i++) {
sumpix[i] = 0;
prodis[i] = 0;
}
//统计灰度级中每个像素在整幅图像中的个数
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
sumpix[(int)GrayImage.at<uchar>(i, j)]++;
}
}
//计算每个灰度级占图像中的概率分布
for (int i = 0; i < 256; i++) {
prodis[i] = (float)sumpix[i] / (cols*rows);
}
//遍历灰度级0到255,计算出最大类间方差下的阈值
float w0, w1, u0_0, u1_1, u0, u1, g;
double maxg = 0;
for (int i = 0; i < 256; i++) {
//每次寻找可能的最大阈值之前需要先对相关参数初始化
w0 = w1 = u0_0 = u1_1 = u0 = u1 = g = 0;
for (int j = 0; j < 256; j++) {
//这里是指的背景
if (j <= i) {
//当这时候的i为阈值时,第一类的总概率,以及第一类中每个灰度级的加权值(方便后面计算出平均灰度级)
w0 += prodis[j];
u0_0 += j * prodis[j];
}
else {
//第二类
w1 += prodis[j];
u1_1 += j * prodis[j];
}
}
//分别计算各类的平均灰度,利用公式u0*w0=u0_0;
u0 = u0_0 / w0;
u1 = u1_1 / w1;
g = (float)(w0*w1*pow((u0 - u1), 2));
//依次比较找到最大类间方差的阈值
if (g > maxg) {
maxg = g;
thresh = i;
}
}
MaxThresh = thresh;
Mat destImage;
threshold(GrayImage, destImage, thresh, 255, THRESH_BINARY);
imshow("OTSU二值化结果图", destImage);
}
最大熵阈值分割
一、 原理
参考上面的otsu的划分前景背景方法,这里我们也需要一种界定手段,来寻找我们期待的阈值,从而求得最大熵。
给定了一个特定的阈值q(即某一灰度值),根据该阈值将图片划分为背景和前景。分别将背景和前景中的每一个灰度级的占图片的概率进行累加,结果记为P0和P1。则背景和前景分别对应的最大熵为:
H0+H1当达到最大时,我们就说 这时候的阈值为分界灰度值,大于此灰度值的为前景,小于的则为背景。
二、 代码实现
//最大熵阈值分割
//该算法所需参数有:前景/背景每个灰度级所占的比例;前景/背景所有灰度级概率总和;计算前景/背景的熵
void Shang(Mat srcImage) {
int cols = srcImage.cols;
int rows = srcImage.rows;
int thresh = 0;
Mat GrayImage;
cvtColor(srcImage, GrayImage, COLOR_BGR2GRAY);
//初始化统计参数
int sumpix[256];
float prodis[256];
for (int i = 0; i < 256; i++) {
sumpix[i] = 0;
prodis[i] = 0;
}
//统计灰度级中每个像素在整幅图像中的个数
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
sumpix[(int)GrayImage.at<uchar>(i, j)]++;
}
}
//计算每个灰度级占图像中的概率分布
for (int i = 0; i < 256; i++) {
prodis[i] = (float)sumpix[i] / (cols*rows);
}
//遍历灰度级0到255,计算
float P0, P1, H0, H1, H;
double maxH = 0;
for (int i = 0; i < 256; i++) {
//每次寻找可能的最大阈值之前需要先对相关参数初始化
P0 = P1 = H0 = H1 = H =0;
for (int j = 0; j < 256; j++) {
//这里是指的背景
if (j < i) {
//当这时候的i为阈值时,第一类的总概率
P0 += prodis[j];
}
else {
//第二类
P1 += prodis[j];
}
}
for (int j = 0; j < 256; j++) {
//这里是指的背景
if (j < i) {
//当这时候的i为阈值时,计算第一类累计概率
if (prodis[j] == 0) continue;
float bizhi0 = prodis[j] / P0;
H0 += -bizhi0 * logf(bizhi0);
}
else {
//第二类
if (prodis[j] == 0) continue;
float bizhi1 = prodis[j] / P1;
H0 += -bizhi1 * logf(bizhi1);
}
}
H = H0 + H1;
//依次比较找到最大类间方差的阈值
if (H > maxH) {
maxH = H;
thresh = i;
}
}
cout << "thresh=" << thresh << endl;
MaxThresh = thresh;
Mat destImage;
threshold(GrayImage, destImage, thresh, 255, THRESH_BINARY);
imshow("熵二值化结果图", destImage);
}