OpenCV中其实有对OTUS算法的实现,threhold()函数最后一个参数可以指定使用OTUS算法。

1、最大间方差(OTUS)算法的描述

和均值迭代算法相似,OTUS算法也是利用图像的直方图进行的。OTUS算法的思想是选取一个阈值$T, T \in [0,m-1]$,$m$为图像的灰度级将直方图两部分,$T$值使得分成的两组间方差最大。

2、算法的步骤

待处理图像灰度值范围为$[0, m-1]$, 记灰度值为$i$的像素个数为$n_{i}$.

  1. 统计待处理图像中像素灰度总和$N$: $$N=\sum_{i=0}^{m-1}n_{i}$$
  2. 计算每个灰度值$i, i \in [0,m-1]$所占的概率:$$p_{i}=\frac{n_{i}}{N}$$灰度均值$\mu$:$$\mu=\sum_{i=0}^{m-1}i*p_{i}$$
  3. 取灰度值$T = 0 ,`1,… m-1$,利用$T$将所有灰度分成两组$C_{0}={0 … T-1}$和$C_{1}={T … m-1}$,两组的均值和概率表示为:
    第一类所占的概率:$$w_{0} = \sum_{i=0}^{T-1}p_{i}$$ 第一类的均值:$$\mu_{0} = \sum_{i=0}^{T-1}\frac{i*p_{i}}{w_{0}}$$ 第二类所占的概率:$$w_{1} = \sum_{i=T}^{m-1}p_{i} = 1-w_{0}$$ 第二类的均值:$$\mu_{1} = \sum_{i=T}^{m-1}\frac{i*p_{i}}{w_{1}}$$
  4. 计算两组间的方差:$$\delta^{2} = w_{0}(\mu_{0}-\mu)^{2} + w_{1}(\mu_{1}-\mu)^{2}$$
  5. 重复过程2 - 4,找到组间方差最大的灰度值$i$,使用$i$对图像进行阈值分割即可。

3、OpenCV下的实现

Mat OstuSeg(Mat src)
{
	int tbHist[256] = {0};						//直方图数组
	double average = 0.0;						//平均像素值
	double cov = 0.0;							//方差
	double maxcov = 0.0;						//方差最大值
	int index = 0;								//分割像素值
	Mat dst;
	int nCol = src.cols * src.channels();		//每行的像素个数
	for (int i = 0; i < src.rows; i++)
	{
		uchar* pData = src.ptr<uchar>(i);
		for (int j = 0; j < nCol; ++j)
		{
			tbHist[pData[j]] += 1;
		}
	}

	int sum = 0;
	for (int i=0; i<256; ++i)					
		sum += tbHist[i];

	double w0=0.0, w1=0.0, u0=0.0, u1=0.0;
	int count0 = 0;
	for (int i = 0; i < 255; ++i)
	{
		u0 = 0;
		count0 = 0;
		for (int j=0; j<=i; ++j)
		{
			u0 += j * tbHist[j];
			count0 += tbHist[j];
		}
		u0 = u0/count0;
		w0 = (float)count0/sum;

		u1 = 0;
		for (int j=i+1; j<256; ++j)
			u1 += j*tbHist[j];

		u1 = u1/(sum - count0);
		w1 = 1 - w0;
		cov = w0*w1*(u1-u0)*(u1-u0);
		if (cov > maxcov)
		{
			maxcov = cov;
			index = i;
		}
	}
	cv::threshold(src, dst, index, 255, 0);    //进行阈值分割
	return dst.clone();
}

4、拓展

和上一篇所述一样,如果我们需要分割的物体并不是规则的矩形,那么任然可以采用在直方图中将我们已知的无效像素去掉。