最大类间方差法(Otsu)
顾名思义该方法利用目标区域与背景区域之间的方差最大的思想,达到分割图像的目的。也就是说,选取最佳阈值T时,目标与背景之间的方差值最大,小于阈值T的区域为D1,大于阈值的区域为D2,如此一来即可将需要的区域区分开来。话不多说,上代码:
//统计灰度直方图,直接在图像转数组中实现
private unsafe void Image2Byte()
{
BitmapData unlock = new BitmapData();
unlock = _bitmap.LockBits(new Rectangle(0, 0, _w, _h),
ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
int stride = unlock.Stride;
byte* temp = (byte*)unlock.Scan0.ToPointer();
for (int j = 0; j < _h; j++)
{
for (int i = 0; i < _w; i++)
{
// gray =R*0.299 + G*0.587 + B*0.114
int gray = temp[0] * 19595 + temp[1] * 38469 + temp[2] * 7472 >> 16;
_grayData[i, j] = (byte)gray;
_grayHistogram[gray]++;
temp += 3;
}
temp += stride - 3 * _w;
}
_bitmap.UnlockBits(unlock);
}
//排除开头和结尾灰度数量为0的部分,减少计算量
private bool NoneZero(int value)
{
return value != 0 ? true : false;
}
/// <summary>
/// 最大类间方差法
/// </summary>
private int Otsu()
{
Image2Byte();
int sumPoint = _grayData.Length; //图像所有点数
double sumGray = 0; //图像总灰度值
for (int i = 0; i < 256; i++)
{
sumGray +=i*_grayHistogram[i];
}
int minGray = Array.FindIndex(_grayHistogram, NoneZero); //灰度非零起始点
int maxGray = Array.FindLastIndex(_grayHistogram, NoneZero);//灰度非零结束点
int threshold = minGray; //初始最大类间方差
double maxVariance = 0; //初始最大方差
double desSumGray = 0; // 初始目标质量矩
double desSumPoint = 0; // 初始目标点数
for (int i = minGray; i < maxGray; i++)
{
if (_grayHistogram[i] == 0) continue;
desSumPoint += _grayHistogram[i]; //目标区域总点数
desSumGray += i * _grayHistogram[i]; //灰度值点数 * 灰度值 = 目标区域总灰度值
double diference = desSumGray/desSumPoint- sumGray / sumPoint;
double variance = (diference * diference )*( desSumPoint / (sumPoint -
desSumPoint));
if (variance > maxVariance)
{
maxVariance = variance;
threshold = i;
}
}
return threshold;
}
总结:该方法在图像灰度直方图呈现单峰时效果较好。但图像在光照不均匀,噪声点大等情况下分割效果不佳,具体应用可参考最大类间方差法的思想,将目标与背景分为两个部分,分别处理,在合一起考虑的思想。
参考文献:[1]Otsu N. A threshold selection method from gray-level histograms[J]. Automatica, 1975, 11(285-296): 23-27.