Niblack算法和OTSU算法的实现

1、OTSU算法是设阈值将图像分割成俩组,一组灰度对应目标,另一组灰度对应背景,则这俩组灰度值的类内方差最小,俩组的类间方差最大。相关公式:
                                                            g = w0 *(u0 - u)*(u0 - u) + w1*(u1 - u)*(u1 - u) = w0 * w1 *(u0 - u1)*(u0 - u1)
     我们将遍历最小灰度值到最大灰度值T作为图像的阈值,如果目标点的灰度值小于阈值则归为背景,大于阈值则归为前景。w0为使用前景T作为阈值是时像素点数比例,u1为使用T作为阈值是前景时平均灰度;w1为使用T作为阈值时背景像素点数比例,u1为使用T作为阈值是背景的平均灰度。而u则可由下式得到:
                                                            u = w0 * u0 + w1 * u1


void Otsu(const IplImage *srcImg,IplImage *dstImg)
{

	int m = 256;
	int *color = new int[m];
    memset(color, 0, m* sizeof(int));
	for(int h = 0; h < srcImg->height; h++)
	{
		uchar *p = (uchar*)(srcImg->imageData + srcImg->widthStep * h);
		for(int w = 0; w < srcImg->width; w++)
		{
			color[p[w]]++;
		}
	}   //统计每个灰度值的像素点数
	double fmax = 0;
	int bestTH = 0;
	int *n = new int[m];
	int *px = new int[m];
    memset(n , 0, m * sizeof(int));
    memset(px, 0, m * sizeof(int));
	n[0] = color[0];
	px[0] = 0;
	for(int i = 1; i < m; i++)
	{
		n[i] = n[i - 1] + color[i];
		px[i] = px[i - 1] + color[i]*i;
	}  //计算总的像素点数和总灰度值
	for(int j = 0; j < m; j++)
	{
		int n1 = n[j];  //n1为在当前阈值前景的像素点数
		if(n1 == 0)
			continue;
		int n2 = n[m-1] - n1; //n2为背景的像素点数
		if(n2 == 0)
			break;  //当n1为0时没有分出前景和后景,n2为0是表示所有像素点全为背景,之后遍历不能再使前景的像素点数增加,故可退出循环
		
		double u1 = px[j]/n1;  //前景的灰度平均值
		double u2 = (px[m-1] - px[j])/n2;  //背景的灰度平均值
		double fsb = n1 * n2 * (u1 - u2) * (u1 - u2);  //两组灰度值对应的类间方差
		if(fsb > fmax)
		{
			fmax = fsb;
			bestTH = j;
		}  //fmax始终为最大类间方差,bestTH为对应的最大类间方差的灰度值即为最佳阈值
	}
	for(int i = 0; i < srcImg->height; i++)
	{
		uchar *srcptr = (uchar*)(srcImg->imageData + srcImg->widthStep * i);
		uchar *dstptr = (uchar*)(dstImg->imageData + dstImg->widthStep * i);
		for(int j = 0;j< srcImg->width; j++)
		{
			if(srcptr[j] > bestTH)
				dstptr[j] = 255;
			else
				dstptr[j] = 0;
		}
	}
    delete [] color;
    delete [] n;
    delete [] px;
}

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

2、Nilblack二值化算法是针对全局阈值发在图像和背景灰度相差明显时容易忽略细节的问题进行优化,是一种常用的比较有效的局部阈值算法。这种算法的基本思想是对预想中的每一个点,在它的R*R领域内,计算领域里的像素点的均值和方差,然后下式计算阈值进行二值化:T(x,y) = m(x,y) + k* s(x,y)其中对于像素点坐标(x,y),T(x,y) 为该点阈值,m(x,y)为该点的R*R领域内像素点的像素均值,s(x,y)为该店R*R领域内像素标准方差,k为修正系数(通常取-0.1)。

void Niblack(const IplImage *srcImg, IplImage *binImg, double k)
	{
		int m = srcImg->width;
		int n = srcImg->height;
		int s = Niblack_s;
		int q = Niblack_q;
		
		for(int i = 0; i < n; i++)
		{
			char *srcptr = (char*)(srcImg->imageData + srcImg->widthStep * i);
			char *dstptr = (char*)(binImg->imageData + binImg->widthStep * i);
			for(int j = 0; j < m; j++)
			{
				int begin_y = i - Niblack_q, begin_x = j - Niblack_s;
				int end_y = i + Niblack_q, end_x = j + Niblack_s;
				if(begin_y < 0) begin_y = 0;
				if(begin_x < 0) begin_x = 0;
				if(end_y > n) end_y = n;
				if(end_x > m) end_x = m;

				double mean = 0;
				double std = 0;
				int total = (end_y - begin_y) * (end_x - begin_x);  //该领域内总的像素点数

				for(int y = begin_y; yimageData + srcImg->widthStep * y);
					for(int x = begin_x; x < end_x; x++)
					{
						mean += ptr[x];
					}
				}  //计算在该小领域内灰度值总和

				mean = mean/total;  //该领域的平均灰度

				for(int y = begin_y; y < end_y; y++)
				{
					char *ptr = (char*)(srcImg->imageData + srcImg->widthStep * y);
					for(int x = begin_x; x < end_x; x++)
					{
						double sq = (mean - ptr[x]) * (mean - ptr[x]);
						std +=sq;
					}
				}   //求出该领域内总的方差和

				std /=total;
				std = sqrt(std);  //求出标准差

				double threshold = mean + k * std;  //求出所得到的的阈值

				int temp = 255;
				if(srcptr[j] > threshold)
					dstptr[j] = temp;
				else
					dstptr[j] = 0;
			}
		}
	}


你可能感兴趣的:(opencv学习~)