C#,用基本全局阈值法求图像二值化时的阀值

最近在学习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); // 二值化

大概就是这样……