这篇主要写图像的二值化处理,图像二值化是指将图像的像素点只有两种情况,0和255,基本思路是先将图像灰度化,这里就不详细讲解了,在我的上一篇里有详细代码,接下来就是对灰度图像的处理。现在从最简单的情况开始,二值化就是要选取一个阈值,这里打个比方阈值是200,灰度值大于200的灰度值令为255,灰度值小于200的令其灰度值为0。
这里我先给出最简单的代码,这里的阈值是凭自己的感觉设置的:
#include <graphics.h>
#include <conio.h>
#include <stdio.h>
#include <math.h>
int image[1500][1000];
IMAGE img1;
int imagHight,imagWidth;
int threshold = 0;//阈值
int main()
{
int i,j,k;
// 读取图片至绘图窗口
loadimage(&img1, _T("D:\\testh.bmp"));
imagHight = img1.getheight();
imagWidth = img1.getwidth();
total = imagHight*imagWidth;
initgraph(imagWidth,imagHight, SHOWCONSOLE|NOCLOSE|NOMINIMIZE);
putimage(0, 0, &img1);
DWORD* pMem = GetImageBuffer();
//图像灰度化
for(i = 0; i <imagHight; i++)
{
for(j=0;j<imagWidth;j++)
{
*pMem = BGR(*pMem);
image[i][j] = (GetRValue(*pMem)*299+GetGValue(*pMem)*587+GetBValue(*pMem)*114+500)/1000;
*pMem = RGB(image[i][j],image[i][j],image[i][j]);
pMem++;
}
}
pMem -= imagHight*imagWidth; //将指针重新指向第一个像素点
//二值化
for(i = 0; i <imagHight; i++)
{
for(j=0;j<imagWidth;j++)
{
if(image[i][j]>200)image[i][j] = 255;
else image[i][j] = 0;
*pMem = RGB(image[i][j],image[i][j],image[i][j]);
pMem++;
}
}
FlushBatchDraw();
_getch();
closegraph();
}
效果图如下:
原图
阈值为200
if(image[i][j]>200)image[i][j] = 255;
这里把上面的200阈值改为50,可以看到,大部分的灰度都大于50,所以图像大部分是白色的(灰度值为255)。
这里也看出阈值选取的重要性,不同的阈值,前景与背景的分离程度是不同的,如何选取最合适的阈值是二值化的关键。
于是我研究了一下otsu二值算法,也叫大津算法,这里我参考了这篇文章,大家可以看一下,里面还是比较容易看懂的。 https://blog.csdn.net/qingzhuyuxian/article/details/88819810#commentBox
这里是我自己写的程序:
#include <graphics.h>
#include <conio.h>
#include <stdio.h>
#include <math.h>
int image[1500][1000];
int Histogram[256]={0};
IMAGE img1;
int imagHight,imagWidth;
double total;//像素点的总数
double variances[256];//每个阈值对应的组内方差
double bvariances;//背景的方差
double fvariances;//前景的方差
double bproportion;//背景的像素点占的比例
double fproportion;//前景的像素点占的比例
double count;//用来记录像素点的个数
double bmean;//背景的平均值
double fmean;//前景的平均值
double min_value;//最小的方差
int threshold = 0;//阈值
int main()
{
int i,j,k;
// 读取图片至绘图窗口
loadimage(&img1, _T("D:\\testh.bmp"));
imagHight = img1.getheight();
imagWidth = img1.getwidth();
total = imagHight*imagWidth;
initgraph(imagWidth,imagHight, SHOWCONSOLE|NOCLOSE|NOMINIMIZE);
putimage(0, 0, &img1);
DWORD* pMem = GetImageBuffer();
_getch();
//图像灰度化
for(i = 0; i <imagHight; i++)
{
for(j=0;j<imagWidth;j++)
{
*pMem = BGR(*pMem);
image[i][j] = (GetRValue(*pMem)*299+GetGValue(*pMem)*587+GetBValue(*pMem)*114+500)/1000;
*pMem = RGB(image[i][j],image[i][j],image[i][j]);
pMem++;
}
}
pMem -= imagHight*imagWidth; //将指针重新指向第一个像素点
//获取直方图
for(i = 0; i <imagHight; i++)
{
for(j=0;j<imagWidth;j++)
{
Histogram[image[i][j]]++;
}
}
//求组内方差最小的那个阈值
for(i=0;i<256;i++) //从阈值为0到255 分别计算对应的组内方差
{
count = 0;
bmean = 0;
bproportion = 0;
bvariances = 0;
for(j=0;j<i;j++)
{
count += Histogram[j];
bmean = Histogram[j]*j;
}
bproportion = count / total;
bmean = (count==0)?0:(bmean/count);
for(k=0;k<i;k++)
{
bvariances += pow(k-bmean,2)*Histogram[k];
}
bvariances = (count==0)?0:bvariances / count;
count = 0;
fmean = 0;
fproportion = 0;
fvariances = 0;
for(j=i;j<256;j++)
{
count += Histogram[j];
fmean = Histogram[j]*j;
}
fproportion = count / total;
fmean = (count==0)?0:(fmean/count);
for(k=i;k<256;k++)
{
fvariances += pow(k-fmean,2)*Histogram[k];
}
fvariances = (count==0)?0:fvariances / count;
variances[i] = bproportion * bvariances + fproportion * fvariances;
}
//找到最小方差值对应的那个阈值
min_value = variances[0];
for(i=1;i<256;i++)
{
if(min_value>variances[i])
{
min_value = variances[i];
threshold = i;
}
}
printf("threshold:%d",threshold); //打印最佳阈值
//二值化
for(i = 0; i <imagHight; i++)
{
for(j=0;j<imagWidth;j++)
{
if(image[i][j]>threshold)image[i][j] = 255;
else image[i][j] = 0;
*pMem = RGB(image[i][j],image[i][j],image[i][j]);
pMem++;
}
}
FlushBatchDraw();
_getch();
closegraph();
}
效果图:
这里计算出的阈值是185
还有其他的求阈值的方法,这里就不列出来了,感兴趣的自己可以查一下。