图像的二值化,通常指灰阶图像转化为二值图像,二值图像即一个pixel只有两种变化全暗(0)或者全亮(255),单独记录二值图像的话就每个Pixel只要1bit就足够了,0或1。
要将一个灰阶图像二值化,你就要能够很好的区分哪些是背景、哪些是前景,或者说哪一部分该变全暗哪一部分该变全亮。这就需要找到一个最好Threshold(阀值),把大于这个阀值的灰度值极大化(全亮),小于这个阀值的灰度值极小化(全暗)。
阀值能我们可以设固定的,也可以用自适应方法。Otsu就是极好的、通过计算找到最佳的阀值的方法。
- Otsu's :
- 通过阀值将一幅图的灰度级分成了两个区域,最佳的阀值说简单点要做到的是:这两个区域,各自内部要越像越好,区域之间要差异越大越好。要看一个区域的pixel像不像,差异大不大用什么?答案很简单就是方差。Otsu's的方法就是要两区域,区域內方差要最小或者区域间方差要最大。
- 方差公式:,x 是均值,n 是数总个数。
- 所以Otsu's 的公式 : ; 和 分别为两区域 pixel 所占的比例权重, 和 就是两区域的pixel內方差了。
- 实现时,只要从0~255灰度级中,计算每一个灰度级的 ,这样找到 最小的那个灰度级,就是最佳的阀值。
C#实现:
//通过Otsu's获得最佳Thresholding;
public int getOPTThresholding(Bitmap grayImage)
{ //计算类内方差之和最小;
//获取按 pixel 个数统计直方图
int[] histogram = getGrayNumbHistogram(grayImage);
int pixelNumb = grayImage.Height * grayImage.Width;
int threshold;
//储存差异集合
double[] variances = new double[256];
//T为0-255,为 Threshold 拟定值.
for (int T = 0; T < histogram.Length; T++)
{
//两区域 pixel 个数,区域 pixel 值总和,区域平均数,区域权重;
double n1 = 0, n2 = 0;
double total1 = 0, total2 = 0;
double aver1 = 0, aver2 = 0;
double w1 = 0, w2 = 0;
//区域 1,2 方差
double ft1 = 0, ft2 = 0;
for (int i = 0; i < T; i++)
{
n1 += histogram[i];
total1 += histogram[i] * i;
}
for (int j = T; j < variances.Length; j++)
{
n2 += histogram[j];
total2 += histogram[j] * j;
}
w1 = n1 / pixelNumb;
w2 = n2 / pixelNumb;
//防止个数為 0,出错;
aver1 = (n1 == 0) ? 0 : (total1 / n1);
aver2 = (n2 == 0) ? 0 : (total2 / n2);
for (int i = 0; i < T; i++)
{
ft1 += (Math.Pow((i - aver1), 2) * histogram[i]);
}
for (int j = T; j < 256; j++)
{
ft2 += (Math.Pow((j - aver2), 2) * histogram[j]);
}
ft1 = (n1 == 0) ? 0 : (ft1 / n1);
ft2 = (n2 == 0) ? 0 : (ft2 / n2);
variances[T] = w1 * ft1 + w2 * ft2;
}
double min = variances[0];
threshold = 0;
for (int i = 1; i < variances.Length; i++)
{
if (variances[i] < min)
{
min = variances[i];
threshold = i;
}
}
return threshold;
}
仅为个人理解,如有不足,请指教。 https://blog.csdn.net/weixin_35811044