この記事では主に画像の2値化処理について説明します。画像の2値化とは、画像のピクセル0と255の2つの場合のみを指します。基本的な考え方は、画像を最初にグレースケール化することです。前の記事では、詳細なコードがあり、次のステップはグレースケール画像を処理することです。ここで、最も単純なケースから、2値化はしきい値を選択することです。ここで、たとえば、しきい値は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)です。
ここでは、しきい値の選択の重要性、異なるしきい値、前景と背景の分離の程度も異なります。最も適切なしきい値を選択する方法が2値化の鍵です。
そこで、大津アルゴリズムとも呼ばれる大津バイナリアルゴリズムを調べましたが、ここではこの記事を参考にしてみてください。 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です
しきい値を見つける方法は他にもありますが、ここにはリストされていません。興味があれば確認できます。