最近在学习Emgu CV,并重新复习C#,在图像处理中,有一个过程叫“二值化”,简单来说就是将图片的每个像素转换成1或0,以方便后面的边缘检测,但是在二值化的过程当中,有个很复杂的问题就是确定二值化的阀值,怎么获得这个阀值呢?肉壳在网上找了一些方法,其中一种叫基本全局阀值法。SkySeraph在他的文章中还提到了其他几种方法,不过都是用C++实现的,因为我比较低端不会C++,所以一直能抽一种自己需要的方法,修改成C#语言了……
因为今天也是我第一次用Emgu,所以不确定最终获得阀值的使用方法是否正确,大家帮忙看一下吧,错了别怪我……
/*=====基本全局阈值法=======*/
int BasicGlobalThreshold(int[] pg, int start, int end)
{
int i, t, t1, t2, k1, k2;
double u, u1, u2;
t = 0;
u = 0;
for (i = start; i < end; i++)
{
t += pg[i];
u += i * pg[i];
}
k2 = (int)(u / t); // 计算此范围灰度的平均值
do
{
k1 = k2;
t1 = 0;
u1 = 0;
for (i = start; i <= k1; i++)
{ // 计算低灰度组的累加和
t1 += pg[i];
u1 += i * pg[i];
}
t2 = t - t1;
u2 = u - u1;
if (t1 != 0)
u1 = u1 / t1; // 计算低灰度组的平均值
else
u1 = 0;
if (t2 != 0)
u2 = u2 / t2; // 计算高灰度组的平均值
else
u2 = 0;
k2 = (int)((u1 + u2) / 2); // 得到新的阈值估计值
} while (k1 != k2); // 数据未稳定,继续
return (k1); // 返回阈值
}
这个是基本算法pg是直方图,start和end表示直方图的起始位置和结束位置,这段代码基本没改动过,只是把C++里的指针给弄没了……
//gray_image是变量
int i, thre;
int[] pg = new int[256];
for (i = 0; i < 256; i++) pg[i] = 0;
int w = gray_image.Width; // 获得图像的长和宽
int h = gray_image.Height;
//直方图统计
int[] hist_size = new int[1] { 256 }; // 建一个数组来存放直方图数据
IntPtr HistImg = CvInvoke.cvCreateHist(1, hist_size, Emgu.CV.CvEnum.HIST_TYPE.CV_HIST_ARRAY, null, 1); // 创建了一个空的直方图
IntPtr[] inPtr1 = new IntPtr[1] { gray_image };
CvInvoke.cvCalcHist(inPtr1, HistImg, false, System.IntPtr.Zero); // 计算inPtr1指向图像的数据
for (i = 0; i < 256; i++)
{
pg[i] = (int) CvInvoke.cvQueryHistValue_1D(HistImg, i); // 将直方图数据放入数组
}
thre = BasicGlobalThreshold(pg, 0, 256); // 确定阈值
CvInvoke.cvThreshold(gray_image, gray_image, thre, 255, Emgu.CV.CvEnum.THRESH.CV_THRESH_BINARY); // 二值化
大概就是这样……